diff --git a/.bandit b/.bandit new file mode 100644 index 0000000..eadf1e4 --- /dev/null +++ b/.bandit @@ -0,0 +1,3 @@ +# FILE: .bandit +[bandit] +skips: ['B607'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index de39134..c74abbd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,7 @@ name: Report a bug description: Tell us about a bug or issue you may have identified in Logicytics. title: "Provide a general summary of the issue" -labels: [ bug ] +labels: ["progress:Unreviewed"] assignees: "DefinetlyNotAI" body: - type: checkboxes @@ -28,7 +28,7 @@ body: label: Debugger Log description: Include the log File generated by running `.\Logicytics --debug`, upload the File contents and paste it in. validations: - required: true + required: false - type: textarea id: b_log attributes: @@ -42,18 +42,19 @@ body: label: Anything else? description: Include anything you deem important. validations: - required: true + required: false - type: dropdown id: browser attributes: - label: What browser(s) are you using? + label: What flags were you using to run Logicytics? multiple: true options: - - Chrome - - Safari - - Firefox - - Microsoft Edge - - Opera + - Threading + - Dev + - Default + - Modded + - Other + - N/A - type: input id: version attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 3abacbd..2054089 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,7 @@ name: Feature request description: Suggest new or updated features to include in Logicytics. title: "Suggest a new feature" -labels: [ feature ] +labels: ["progress:Unreviewed"] assignees: "DefinetlyNotAI" body: - type: checkboxes @@ -13,7 +13,7 @@ body: required: true - label: I have read the [contributing guidelines](https://github.com/DefinetlyNotAI/Logicytics/blob/main/CONTRIBUTING.md) required: true - - label: I would like to contribute this request. + - label: I would like to contribute this request (Optional). required: false - type: textarea id: proposal @@ -27,6 +27,8 @@ body: attributes: label: Motivation and context description: Tell us why this change is needed or helpful, and what problems it may help solve. + validations: + required: true - type: dropdown id: importance attributes: @@ -36,6 +38,6 @@ body: - High - Normal - Low - - Unknown + - N/A validations: required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e66520b..b8cbf35 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,6 +3,7 @@ ### Prerequisites + - [ ] I have [searched](https://github.com/DefinetlyNotAI/Logicytics/pulls) for duplicate or closed issues. - [ ] I have read the [contributing guidelines](https://github.com/DefinetlyNotAI/Logicytics/blob/main/CONTRIBUTING.md). @@ -14,26 +15,29 @@ ### PR Type + - [ ] Bug fix - [ ] New feature - [ ] Refactoring - [ ] Documentation update -- [ ] ⚠️ Breaking change ⚠️ +- [ ] ⚠️ Breaking change ⚠️ ### Description - + ### Motivation and Context - + ### Binaries - + + + ### Issues Fixed - + diff --git a/.github/config.yml b/.github/config.yml index 0c70183..e12c499 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -5,12 +5,6 @@ requestInfoReplyComment: > We would appreciate it if you could provide us with more info about this issue/pr! We hate to have to ask so much. But to help us improve, please provide us with more information. -# Or an array: -# requestInfoReplyComment: -# - Ah no! young blade! That was a trifle short! -# - Tell me more ! -# - I am sure you can be more effusive - # *OPTIONAL* default titles to check against for lack of descriptiveness # MUST BE ALL LOWERCASE @@ -18,9 +12,10 @@ requestInfoDefaultTitles: - update readme.md - updates - Provide a general summary of the issue + - Update # *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given -requestInfoLabelToAdd: Needs More Info +requestInfoLabelToAdd: progress:Invalid # *OPTIONAL* Require Issues to contain more information than what is provided in the issue templates # Will fail if the issue's body is equal to a provided template diff --git a/.github/weekly-digest.yml b/.github/weekly-digest.yml deleted file mode 100644 index fe502fb..0000000 --- a/.github/weekly-digest.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Configuration for weekly-digest - https://github.com/apps/weekly-digest -publishDay: sun -canPublishIssues: true -canPublishPullRequests: true -canPublishContributors: true -canPublishStargazers: true -canPublishCommits: true diff --git a/.gitignore b/.gitignore index dce25a6..3b83a8d 100644 --- a/.gitignore +++ b/.gitignore @@ -309,6 +309,6 @@ $RECYCLE.BIN/ *.lnk # End of https://www.toptal.com/developers/gitignore/api/windows,pycharm,python -/SYSTEM/secret -/CODE/secret -*.pyc \ No newline at end of file +*.pyc +/CODE/SysInternal_Suite/.sys.ignore +/ACCESS/ diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml index 4bbcabe..7b4a032 100644 --- a/.idea/jsonSchemas.xml +++ b/.idea/jsonSchemas.xml @@ -39,6 +39,24 @@ + + + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml index 3d9b9d6..800e363 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,8 @@ + - + \ No newline at end of file diff --git a/.idea/webResources.xml b/.idea/webResources.xml index 30b55eb..299e79f 100644 --- a/.idea/webResources.xml +++ b/.idea/webResources.xml @@ -8,6 +8,7 @@ + diff --git a/CODE/Logicytics.py b/CODE/Logicytics.py index ce681cb..6097abb 100644 --- a/CODE/Logicytics.py +++ b/CODE/Logicytics.py @@ -1,179 +1,19 @@ -import ctypes -import os.path import threading -import zipfile - -from __lib_actions import * -from __lib_log import Log from _debug import debug -from _dev import dev_checks, open_file -from _extra import unzip, menu +from _extra import menu, unzip from _health import backup, update from _hide_my_tracks import attempt_hide -from _zipper import zip_and_hash - - -class Check: - def __init__(self): - """ - Initializes an instance of the class. - - Sets the Actions attribute to an instance of the Actions class. - """ - self.Actions = Actions() - - @staticmethod - def admin() -> bool: - """ - Check if the current user has administrative privileges. - - Returns: - bool: True if the user is an admin, False otherwise. - """ - try: - return ctypes.windll.shell32.IsUserAnAdmin() - except AttributeError: - return False - - def uac(self) -> bool: - """ - Check if User Account Control (UAC) is enabled on the system. - - This function runs a PowerShell command to retrieve the value of the EnableLUA registry key, - which indicates whether UAC is enabled. It then returns True if UAC is enabled, False otherwise. - - Returns: - bool: True if UAC is enabled, False otherwise. - """ - value = self.Actions.run_command( - r"powershell (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).EnableLUA" - ) - return int(value.strip("\n")) == 1 - - @staticmethod - def sys_internal_zip(): - # TODO Test me - try: - ignore_file = os.path.exists("SysInternal_Suite/.ignore") - zip_file = os.path.exists("SysInternal_Suite/SysInternal_Suite.zip") - - if zip_file and not ignore_file: - print("Extracting SysInternal_Suite zip") - with zipfile.ZipFile( - "SysInternal_Suite/SysInternal_Suite.zip" - ) as zip_ref: - zip_ref.extractall("SysInternal_Suite") - - elif ignore_file: - print("Found .ignore file, skipping SysInternal_Suite zip extraction") - - except Exception as err: - print(f"Failed to unzip SysInternal_Suite: {err}", "_L", "G", "CS") - exit(f"Failed to unzip SysInternal_Suite: {err}") - - -class Execute: - @staticmethod - def get_files(directory: str, file_list: list) -> list: - """ - Retrieves a list of files in the specified directory that have the specified extensions. - Parameters: - directory (str): The path of the directory to search. - file_list (list): The list to append the filenames to. - Returns: - list: The list of filenames with the specified extensions. - """ - for filename in os.listdir(directory): - if ( - filename.endswith((".py", ".exe", ".ps1", ".bat")) - and not filename.startswith("_") - and filename != "Logicytics.py" - ): - file_list.append(filename) - return file_list - - def file(self, Index: int) -> None: - """ - Executes a file from the execution list at the specified index. - Parameters: - Index (int): The index of the file to be executed in the execution list. - Returns: - None - """ - self.execute_script(execution_list[Index]) - log.info(f"{execution_list[Index]} executed") - - def execute_script(self, script: str) -> None: - """ - Executes a script file and handles its output based on the file extension. - Parameters: - script (str): The path of the script file to be executed. - Returns: - None - """ - if script.endswith(".ps1"): - self.__run_ps1_script(script) - self.__run_other_script(script) - elif script.endswith(".py"): - self.__run_python_script(script) - else: - self.__run_other_script(script) - - @staticmethod - def __run_ps1_script(script: str) -> None: - """ - Unblocks and runs a PowerShell (.ps1) script. - Parameters: - script (str): The path of the PowerShell script. - Returns: - None - """ - try: - unblock_command = f'powershell.exe -Command "Unblock-File -Path {script}"' - subprocess.run(unblock_command, shell=True, check=True) - log.info("PS1 Script unblocked.") - except Exception as err: - log.critical(f"Failed to unblock script: {err}", "_L", "G", "E") - - @staticmethod - def __run_python_script(script: str) -> None: - """ - Runs a Python (.py) script. - Parameters: - script (str): The path of the Python script. - Returns: - None - """ - result = subprocess.Popen( - ["python", script], stdout=subprocess.PIPE - ).communicate()[0] - print(result.decode()) - - @staticmethod - def __run_other_script(script: str) -> None: - """ - Runs a script with other extensions and logs output based on its content. - Parameters: - script (str): The path of the script. - Returns: - None - """ - result = subprocess.Popen( - ["powershell.exe", ".\\" + script], stdout=subprocess.PIPE - ).communicate()[0] - lines = result.decode().splitlines() - ID = next((line.split(":")[0].strip() for line in lines if ":" in line), None) - - log_funcs = { - "INFO": log.info, - "WARNING": log.warning, - "ERROR": log.error, - "CRITICAL": log.critical, - None: log.debug, - } - - log_func = log_funcs.get(ID, log.debug) - log_func("\n".join(lines)) +from _zipper import Zip +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} """ @@ -195,16 +35,22 @@ def __run_other_script(script: str) -> None: """ # Initialization -Actions().mkdir() +Actions.mkdir() check_status = Check() -try: - # Get flags - action, sub_action = Actions().flags() -except Exception: - action = Actions().flags() - action = action[0] - sub_action = None +if isinstance(Actions().flags(), tuple): + try: + # Get flags + action, sub_action = Actions().flags() + except Exception: + action = Actions().flags() + action = action[0] + sub_action = None +else: + parser = Actions().flags() + parser.print_help() + input("Press Enter to exit...") + exit(1) # Special actions -> Quit if action == "debug": @@ -212,11 +58,10 @@ def __run_other_script(script: str) -> None: input("Press Enter to exit...") exit(0) -log = Log(debug=DEBUG) check_status.sys_internal_zip() if action == "dev": - dev_checks() + Execute().execute_script("_dev.py") input("Press Enter to exit...") exit(0) @@ -228,7 +73,7 @@ def __run_other_script(script: str) -> None: if action == "update": log.info("Updating...") - update() + log.info(update()) log.info("Update complete!") input("Press Enter to exit...") exit(0) @@ -238,7 +83,7 @@ def __run_other_script(script: str) -> None: "Sorry, this feature is yet to be implemented. You can manually Restore your backups, We will open " "the location for you" ) - open_file("../ACCESS/BACKUP/") + Actions().open_file("../ACCESS/BACKUP/") input("Press Enter to exit...") exit(1) @@ -246,7 +91,7 @@ def __run_other_script(script: str) -> None: log.info("Backing up...") backup(".", "Default_Backup") log.debug("Backup complete -> CODE dir") - backup(".", "Mods_Backup") + backup("../MODS", "Mods_Backup") log.debug("Backup complete -> MODS dir") log.info("Backup complete!") input("Press Enter to exit...") @@ -270,8 +115,11 @@ def __run_other_script(script: str) -> None: # Check for privileges and errors if not check_status.admin(): log.critical("Please run this script with admin privileges", "_L", "P", "BA") - input("Press Enter to exit...") - exit(1) + if not DEBUG: + input("Press Enter to exit...") + exit(1) + else: + log.warning("Running in debug mode, continuing without admin privileges") if check_status.uac(): log.warning("UAC is enabled, this may cause issues") @@ -294,6 +142,7 @@ def __run_other_script(script: str) -> None: "netadapter.ps1", "property_scraper.ps1", "window_feature_miner.ps1", + "wifi_stealer.py", ] if action == "minimal": @@ -323,10 +172,17 @@ def __run_other_script(script: str) -> None: # Check weather to use threading or not if action == "threaded": + log.warning("Threading does not support sensitive data miner yet, ignoring") execution_list.remove("sensitive_data_miner.py") threads = [] for index, file in enumerate(execution_list): - thread = threading.Thread(target=Execute().file, args=(index,)) + thread = threading.Thread( + target=Execute().file, + args=( + execution_list, + index, + ), + ) threads.append(thread) thread.start() @@ -337,11 +193,26 @@ def __run_other_script(script: str) -> None: Execute().execute_script(execution_list[file]) log.info(f"{execution_list[file]} executed") - # Zip generated files - +# Zip generated files if action == "modded": + zip_loc_mod, hash_loc, deleted_files_zip, deleted_files_hash = zip_and_hash("..\\MODS", "MODS", action) log.info(zip_loc_mod) + zip_values = Zip().and_hash("..\\MODS", "MODS", action) + if isinstance(zip_values, str): + log.error(zip_values) + else: + zip_loc_mod, hash_loc = zip_values + log.info(zip_loc_mod) + log.debug(hash_loc) + +zip_values = Zip().and_hash(".", "CODE", action) +if isinstance(zip_values, str): + # If error, log it + log.error(zip_values) +else: + zip_loc, hash_loc = zip_values + log.info(zip_loc) log.debug(hash_loc) log.debug(deleted_files_zip) log.debug(deleted_files_hash) @@ -359,19 +230,13 @@ def __run_other_script(script: str) -> None: log.info("Completed successfully") if sub_action == "shutdown": log.info("Shutting down...") - os.system("shutdown /s /t 0") + subprocess.call("shutdown /s /t 3", shell=False) if sub_action == "reboot": log.info("Rebooting...") - os.system("shutdown /r /t 0") + subprocess.call("shutdown /r /t 3", shell=False) if sub_action == "webhook": - log.warning("This feature is not fully implemented yet") - """ - log.info("Sending webhook...") - if WEBHOOK is None or WEBHOOK == "": - log.critical("WEBHOOK URL not set and the request action was webhook", "_L", "P", "BA") - input("Press Enter to exit...") - exit(1) - """ + # Implement this in future + log.warning("This feature is not fully implemented yet! Sorry") log.info("Exiting...") input("Press Enter to exit...") diff --git a/CODE/__lib_actions.py b/CODE/__lib_actions.py deleted file mode 100644 index 3e84822..0000000 --- a/CODE/__lib_actions.py +++ /dev/null @@ -1,271 +0,0 @@ -import argparse -import json -import os -import subprocess -from subprocess import CompletedProcess -from pathlib import Path - - -class Actions: - @staticmethod - def run_command(command: str) -> str: - """ - Runs a command in a subprocess and returns the output as a string. - - Parameters: - command (str): The command to be executed. - - Returns: - CompletedProcess.stdout: The output of the command as a string. - """ - process = subprocess.run(command, capture_output=True, text=True) - return process.stdout - - @staticmethod - def flags() -> tuple[str, ...]: - """ - A static method that defines and parses command-line Flags for the Logicytics application. - - The Flags method uses the argparse library to define a range of Flags that can be used to customize the - behavior of the application. - - The method checks for exclusivity rules and ensures that only one flag is used, unless the --reboot, - --shutdown, or --webhook Flags are used, in which case only two Flags are allowed. - - The method returns a tuple of the keys of the Flags that were used, or exits the application if the Flags are - invalid. - - Parameters: - None - - Returns: - tuple: A tuple of the keys of the Flags that were used. - """ - # Define the argument parser - parser = argparse.ArgumentParser( - description="Logicytics, The most powerful tool for system data analysis." - ) - # Define Flags - parser.add_argument( - "--default", action="store_true", help="Runs Logicytics default" - ) - parser.add_argument( - "--minimal", - action="store_true", - help="Run Logicytics in minimal mode. Just bare essential scraping", - ) - parser.add_argument( - "--unzip-extra", - action="store_true", - help="Unzip the extra directory zip File - Use on your own device only -.", - ) - parser.add_argument( - "--backup", - action="store_true", - help="Backup Logicytics files to the ACCESS/BACKUPS directory - Use on your own device only -.", - ) - parser.add_argument( - "--restore", - action="store_true", - help="Restore Logicytics files from the ACCESS/BACKUPS directory - Use on your own device only -.", - ) - parser.add_argument( - "--update", - action="store_true", - help="Update Logicytics from GitHub - Use on your own device only -.", - ) - parser.add_argument( - "--extra", - action="store_true", - help="Open the extra directory for more tools.", - ) - parser.add_argument( - "--dev", - action="store_true", - help="Run Logicytics developer mod, this is only for people who want to register their contributions " - "properly. - Use on your own device only -.", - ) - parser.add_argument( - "--exe", - action="store_true", - help="Run Logicytics using its precompiled exe's, These may be outdated and not the best, use only if the " - "device doesnt have python installed.", - ) - parser.add_argument( - "--debug", - action="store_true", - help="Runs the Debugger, Will check for any issues, warning etc, useful for debugging and issue reporting", - ) - parser.add_argument( - "--modded", - action="store_true", - help="Runs the normal Logicytics, as well as any File in the MODS directory, Useful for custom scripts", - ) - parser.add_argument( - "--threaded", - action="store_true", - help="Runs Logicytics using threads, where it runs in parallel", - ) - parser.add_argument( - "--webhook", - action="store_true", - help="Execute Flag that will send zip File via webhook", - ) - parser.add_argument( - "--reboot", - action="store_true", - help="Execute Flag that will reboot the device afterward", - ) - parser.add_argument( - "--shutdown", - action="store_true", - help="Execute Flag that will shutdown the device afterward", - ) - args = parser.parse_args() - special_flag_used = False - - empty_check = ( - str(args) - .removeprefix("Namespace(") - .removesuffix(")") - .replace("=", " = ") - .replace(",", " ") - .split(" ") - ) - if "True" not in empty_check: - parser.print_help() - input("Press Enter to exit...") - exit(1) - - # Check for exclusivity rules - if args.reboot or args.shutdown or args.webhook: - if not ( - args.default or args.threaded or args.modded or args.minimal or args.exe - ): - print( - "--reboot and --shutdown and --webhook Flags require at least one of the following Flags: " - "--default, --threaded, --modded, --minimal, --exe." - ) - input("Press Enter to exit...") - exit(1) - else: - special_flag_used = True - - if not special_flag_used: - # Ensure only one flag is used - used_flags = [flag for flag in vars(args) if getattr(args, flag)] - if len(used_flags) > 1: - print("Only one flag is allowed.") - input("Press Enter to exit...") - exit(1) - else: - # Ensure only 2 Flags is used - used_flags = [flag for flag in vars(args) if getattr(args, flag)] - if len(used_flags) > 2: - print( - "Only one flag is allowed with the --reboot and --shutdown and --webhook Flags." - ) - input("Press Enter to exit...") - exit(1) - - # Set Flags to True or False based on whether they were used - Flags = {key: getattr(args, key) for key in vars(args)} - - # Initialize an empty list to store the keys with values set to True - true_keys = [] - - # Iterate through the Flags dictionary - for key, value in Flags.items(): - # Check if the value is True and add the key to the list - if value: - true_keys.append(key) - # Stop after adding two keys - if len(true_keys) == 2: - break - - # Convert the list to a tuple and return it - if len(tuple(true_keys)) < 3: - return tuple(true_keys) - else: - print( - "Only one flag is allowed with the --reboot and --shutdown and --webhook Flags." - ) - input("Press Enter to exit...") - exit(1) - - @staticmethod - def read_config() -> tuple[str, bool, str, str, list[str]]: - """ - Reads the configuration from the config.json file. - - Returns: - A tuple containing the webhook URL, debug mode, version, API key, and a list of current files. - The types of the returned values are: - - webhook_url: str - - debug: bool - - version: str - - api_key: str - - files: list[str] - - Raises: - FileNotFoundError: If the config.json file is not found. - SystemExit: If the config.json file has an invalid format. - """ - try: - script_dir = Path(__file__).parent.absolute() - config_path = script_dir / "config.json" - with open(config_path, "r") as file: - data = json.load(file) - - webhook_url = data.get("WEBHOOK_URL", "") - debug = data.get("DEBUG", False) - version = data.get("VERSION", "2.0.0") - api_key = data.get("ipgeolocation.io API KEY", "") - files = data.get("CURRENT_FILES", []) - - if not ( - isinstance(webhook_url, str) - and isinstance(debug, bool) - and isinstance(version, str) - and isinstance(api_key, str) - and isinstance(files, list) - ): - print("Invalid config.json format.") - input("Press Enter to exit...") - exit(1) - - return webhook_url, debug, version, api_key, files - except FileNotFoundError: - print("The config.json File is not found.") - input("Press Enter to exit...") - exit(1) - - @staticmethod - def check_current_files(directory: str) -> list: - """ - Retrieves a list of files with specific extensions within a specified directory and its subdirectories. - - Args: - directory (str): The path to the directory to search for files. - - Returns: - list: A list of file paths with the following extensions: .py, .exe, .ps1, .bat. - """ - file = [] - for root, _, filenames in os.walk(directory): - for filename in filenames: - if filename.endswith((".py", ".exe", ".ps1", ".bat")): - files_path = os.path.join(root, filename) - file.append(files_path.removeprefix(".\\")) - return file - - @staticmethod - def mkdir(): - os.makedirs("../ACCESS/LOGS/", exist_ok=True) - os.makedirs("../ACCESS/LOGS/DEBUG", exist_ok=True) - os.makedirs("../ACCESS/BACKUP/", exist_ok=True) - os.makedirs("../ACCESS/DATA/Hashes", exist_ok=True) - os.makedirs("../ACCESS/DATA/Zip", exist_ok=True) - - -WEBHOOK, DEBUG, VERSION, API_KEY, CURRENT_FILES = Actions().read_config() diff --git a/CODE/__lib_class.py b/CODE/__lib_class.py new file mode 100644 index 0000000..8ae39c8 --- /dev/null +++ b/CODE/__lib_class.py @@ -0,0 +1,466 @@ +from __future__ import annotations +import argparse +import json +import os +import subprocess +import ctypes +import os.path +import zipfile +from subprocess import CompletedProcess +from pathlib import Path +from __lib_log import Log + + +class Actions: + @staticmethod + def open_file(file: str): + """ + Opens a specified file using its default application in a cross-platform manner. + Args: + file (str): The path to the file to be opened. + Returns: + None + """ + if not file == "": + file_path = os.path.realpath(file) + try: + subprocess.call(file_path, shell=False) + except Exception as e: + print(f"Error opening file: {e}") + + @staticmethod + def run_command(command: str) -> str: + """ + Runs a command in a subprocess and returns the output as a string. + + Parameters: + command (str): The command to be executed. + + Returns: + CompletedProcess.stdout: The output of the command as a string. + """ + process = subprocess.run(command, capture_output=True, text=True) + return process.stdout + + @staticmethod + def __parse_arguments() -> tuple[argparse.Namespace, argparse.ArgumentParser]: + """ + A static method used to parse command-line arguments for the Logicytics application. + + It defines various flags that can be used to customize the behavior of the application, + including options for running in default or minimal mode, unzipping extra files, + backing up or restoring data, updating from GitHub, and more. + + The method returns a tuple containing the parsed arguments and the argument parser object. + + Returns: + tuple[argparse.Namespace, argparse.ArgumentParser]: A tuple containing the parsed arguments and the argument parser object. + """ + # Define the argument parser + parser = argparse.ArgumentParser( + description="Logicytics, The most powerful tool for system data analysis." + ) + # Define Flags + parser.add_argument( + "--default", action="store_true", help="Runs Logicytics default" + ) + parser.add_argument( + "--minimal", + action="store_true", + help="Run Logicytics in minimal mode. Just bare essential scraping", + ) + parser.add_argument( + "--unzip-extra", + action="store_true", + help="Unzip the extra directory zip File - Use on your own device only -.", + ) + parser.add_argument( + "--backup", + action="store_true", + help="Backup Logicytics files to the ACCESS/BACKUPS directory - Use on your own device only -.", + ) + parser.add_argument( + "--restore", + action="store_true", + help="Restore Logicytics files from the ACCESS/BACKUPS directory - Use on your own device only -.", + ) + parser.add_argument( + "--update", + action="store_true", + help="Update Logicytics from GitHub - Use on your own device only -.", + ) + parser.add_argument( + "--extra", + action="store_true", + help="Open the extra directory for more tools.", + ) + parser.add_argument( + "--dev", + action="store_true", + help="Run Logicytics developer mod, this is only for people who want to register their contributions " + "properly. - Use on your own device only -.", + ) + parser.add_argument( + "--exe", + action="store_true", + help="Run Logicytics using its precompiled exe's, These may be outdated and not the best, use only if the " + "device doesnt have python installed.", + ) + parser.add_argument( + "--debug", + action="store_true", + help="Runs the Debugger, Will check for any issues, warning etc, useful for debugging and issue reporting", + ) + parser.add_argument( + "--modded", + action="store_true", + help="Runs the normal Logicytics, as well as any File in the MODS directory, Useful for custom scripts", + ) + parser.add_argument( + "--threaded", + action="store_true", + help="Runs Logicytics using threads, where it runs in parallel", + ) + parser.add_argument( + "--webhook", + action="store_true", + help="Execute Flag that will send zip File via webhook", + ) + parser.add_argument( + "--reboot", + action="store_true", + help="Execute Flag that will reboot the device afterward", + ) + parser.add_argument( + "--shutdown", + action="store_true", + help="Execute Flag that will shutdown the device afterward", + ) + return parser.parse_args(), parser + + @staticmethod + def __exclusivity(args: argparse.Namespace) -> bool: + """ + Checks if exclusive flags are used in the provided arguments. + + Args: + args (argparse.Namespace): The arguments to be checked. + + Returns: + bool: True if exclusive flags are used, False otherwise. + """ + special_flag_used = False + if args.reboot or args.shutdown or args.webhook: + if not ( + args.default or args.threaded or args.modded or args.minimal or args.exe + ): + print("Invalid combination of flags.") + exit(1) + special_flag_used = True + return special_flag_used + + @staticmethod + def __set_flags(args: argparse.Namespace) -> tuple[str, ...]: + """ + Sets flags based on the provided arguments. + + Args: + args (argparse.Namespace): The arguments to be checked for flags. + + Returns: + tuple[str, ...]: A tuple of flag names that are set to True. + """ + Flags = {key: getattr(args, key) for key in vars(args)} + true_keys = [] + for key, value in Flags.items(): + if value: + true_keys.append(key) + if len(true_keys) == 2: + break + return tuple(true_keys) + + def flags(self) -> tuple[str, ...] | argparse.ArgumentParser: + """ + Handles the parsing and validation of command-line flags. + + Returns either a tuple of used flag names or an ArgumentParser instance. + """ + args, parser = self.__parse_arguments() + special_flag_used = self.__exclusivity(args) + + if not special_flag_used: + used_flags = [flag for flag in vars(args) if getattr(args, flag)] + if len(used_flags) > 1: + print("Only one flag is allowed.") + exit(1) + + if special_flag_used: + used_flags = self.__set_flags(args) + if len(used_flags) > 2: + print("Invalid combination of flags.") + exit(1) + + if not tuple(used_flags): + return parser + else: + return tuple(used_flags) + + @staticmethod + def read_config() -> tuple[str, bool, str, str, list[str]]: + """ + Reads the configuration from the config.json file. + + Returns: + A tuple containing the webhook URL, debug mode, version, API key, and a list of current files. + The types of the returned values are: + - webhook_url: str + - debug: bool + - version: str + - api_key: str + - files: list[str] + + Raises: + FileNotFoundError: If the config.json file is not found. + SystemExit: If the config.json file has an invalid format. + """ + try: + script_dir = Path(__file__).parent.absolute() + config_path = script_dir / "config.json" + with open(config_path, "r") as file: + data = json.load(file) + + webhook_url = data.get("WEBHOOK_URL", "") + debug = data.get("DEBUG", False) + version = data.get("VERSION", "2.0.0") + api_key = data.get("ipgeolocation.io API KEY", "") + files = data.get("CURRENT_FILES", []) + + if not ( + isinstance(webhook_url, str) + and isinstance(debug, bool) + and isinstance(version, str) + and isinstance(api_key, str) + and isinstance(files, list) + ): + print("Invalid config.json format.") + input("Press Enter to exit...") + exit(1) + + return webhook_url, debug, version, api_key, files + except FileNotFoundError: + print("The config.json File is not found.") + input("Press Enter to exit...") + exit(1) + + @staticmethod + def check_current_files(directory: str) -> list: + """ + Retrieves a list of files with specific extensions within a specified directory and its subdirectories. + + Args: + directory (str): The path to the directory to search for files. + + Returns: + list: A list of file paths with the following extensions: .py, .exe, .ps1, .bat. + """ + file = [] + for root, _, filenames in os.walk(directory): + for filename in filenames: + if filename.endswith((".py", ".exe", ".ps1", ".bat")): + files_path = os.path.join(root, filename) + file.append(files_path.removeprefix(".\\")) + return file + + @staticmethod + def mkdir(): + """ + Creates the necessary directories for storing logs, backups, and data. + + Returns: + None + """ + os.makedirs("../ACCESS/LOGS/", exist_ok=True) + os.makedirs("../ACCESS/LOGS/DEBUG", exist_ok=True) + os.makedirs("../ACCESS/BACKUP/", exist_ok=True) + os.makedirs("../ACCESS/DATA/Hashes", exist_ok=True) + os.makedirs("../ACCESS/DATA/Zip", exist_ok=True) + + +class Check: + def __init__(self): + """ + Initializes an instance of the class. + + Sets the Actions attribute to an instance of the Actions class. + """ + self.Actions = Actions() + + @staticmethod + def admin() -> bool: + """ + Check if the current user has administrative privileges. + + Returns: + bool: True if the user is an admin, False otherwise. + """ + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except AttributeError: + return False + + def uac(self) -> bool: + """ + Check if User Account Control (UAC) is enabled on the system. + + This function runs a PowerShell command to retrieve the value of the EnableLUA registry key, + which indicates whether UAC is enabled. It then returns True if UAC is enabled, False otherwise. + + Returns: + bool: True if UAC is enabled, False otherwise. + """ + value = self.Actions.run_command( + r"powershell (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).EnableLUA" + ) + return int(value.strip("\n")) == 1 + + @staticmethod + def sys_internal_zip(): + """ + Extracts the SysInternal_Suite zip file if it exists and is not ignored. + + This function checks if the SysInternal_Suite zip file exists and if it is not ignored. + If the zip file exists and is not ignored, + it extracts its contents to the SysInternal_Suite directory. + If the zip file is ignored, it prints a message indicating that it is skipping the extraction. + + Raises: + Exception: If there is an error during the extraction process. The error message is printed to the console and the program exits. + """ + try: + ignore_file = os.path.exists("SysInternal_Suite/.sys.ignore") + zip_file = os.path.exists("SysInternal_Suite/SysInternal_Suite.zip") + + if zip_file and not ignore_file: + print("Extracting SysInternal_Suite zip") + with zipfile.ZipFile( + "SysInternal_Suite/SysInternal_Suite.zip" + ) as zip_ref: + zip_ref.extractall("SysInternal_Suite") + + elif ignore_file: + print( + "Found .sys.ignore file, skipping SysInternal_Suite zip extraction" + ) + + except Exception as err: + print(f"Failed to unzip SysInternal_Suite: {err}", "_L", "G", "CS") + exit(f"Failed to unzip SysInternal_Suite: {err}") + + +class Execute: + @staticmethod + def get_files(directory: str, file_list: list) -> list: + """ + Retrieves a list of files in the specified directory that have the specified extensions. + Parameters: + directory (str): The path of the directory to search. + file_list (list): The list to append the filenames to. + Returns: + list: The list of filenames with the specified extensions. + """ + for filename in os.listdir(directory): + if ( + filename.endswith((".py", ".exe", ".ps1", ".bat")) + and not filename.startswith("_") + and filename != "Logicytics.py" + ): + file_list.append(filename) + return file_list + + def file(self, execution_list: list, Index: int): + # IT IS USED, DO NOT REMOVE + """ + Executes a file from the execution list at the specified index. + Parameters: + Index (int): The index of the file to be executed in the execution list. + execution_list (list): List to use when indexing + Returns: + None + """ + self.execute_script(execution_list[Index]) + Log().info(f"{execution_list[Index]} executed") + + def execute_script(self, script: str): + """ + Executes a script file and handles its output based on the file extension. + Parameters: + script (str): The path of the script file to be executed. + Returns: + None + """ + if script.endswith(".ps1"): + self.__unblock_ps1_script(script) + self.__run_other_script(script) + elif script.endswith(".py"): + self.__run_python_script(script) + else: + self.__run_other_script(script) + + @staticmethod + def __unblock_ps1_script(script: str): + """ + Unblocks and runs a PowerShell (.ps1) script. + Parameters: + script (str): The path of the PowerShell script. + Returns: + None + """ + try: + unblock_command = f'powershell.exe -Command "Unblock-File -Path {script}"' + subprocess.run(unblock_command, shell=False, check=True) + Log().info("PS1 Script unblocked.") + except Exception as err: + Log().critical(f"Failed to unblock script: {err}", "_L", "G", "E") + + @staticmethod + def __run_python_script(script: str): + """ + Runs a Python (.py) script. + Parameters: + script (str): The path of the Python script. + Returns: + None + """ + result = subprocess.Popen( + ["python", script], stdout=subprocess.PIPE + ).communicate()[0] + print(result.decode()) + + @staticmethod + def __run_other_script(script: str): + """ + Runs a script with other extensions and logs output based on its content. + Parameters: + script (str): The path of the script. + Returns: + None + """ + result = subprocess.Popen( + ["powershell.exe", ".\\" + script], stdout=subprocess.PIPE + ).communicate()[0] + lines = result.decode().splitlines() + ID = next((line.split(":")[0].strip() for line in lines if ":" in line), None) + + log_funcs = { + "INFO": Log().info, + "WARNING": Log().warning, + "ERROR": Log().error, + "CRITICAL": Log().critical, + None: Log().debug, + } + + log_func = log_funcs.get(ID, Log().debug) + log_func("\n".join(lines).removeprefix(ID or "")) + + +WEBHOOK, DEBUG, VERSION, API_KEY, CURRENT_FILES = Actions.read_config() diff --git a/CODE/__lib_log.py b/CODE/__lib_log.py index 4a15342..5a2eda7 100644 --- a/CODE/__lib_log.py +++ b/CODE/__lib_log.py @@ -1,7 +1,6 @@ import os import pathlib from datetime import datetime - import colorlog @@ -33,14 +32,16 @@ def __init__( If you are using colorlog, DO NOT INITIALIZE IT MANUALLY, USE THE LOG CLASS PARAMETER'S INSTEAD. Sorry for any inconvenience that may arise. - Args: filename (str, optional): The name of the log File. Defaults to "Server.log". use_colorlog (bool, - optional): Whether to use colorlog. Defaults to True. debug (bool, optional): Whether to use the DEBUG level. - Defaults to False (which uses the INFO level). debug_color (str, optional): The color of the DEBUG level. - Defaults to "cyan". info_color (str, optional): The color of the info level. Defaults to "green". - warning_color (str, optional): The color of the warning level. Defaults to "yellow". error_color (str, - optional): The color of the error level. Defaults to "red". critical_color (str, optional): The color of the - critical level. Defaults to "red". colorlog_fmt_parameters (str, optional): The format of the log message. - Defaults to "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s". + Args: + filename (str, optional): The name of the log File. Defaults to "Server.log". + use_colorlog (bool, optional): Whether to use colorlog. Defaults to True. + debug (bool, optional): Whether to use the DEBUG level. Defaults to False (which uses the INFO level). + debug_color (str, optional): The color of the DEBUG level. Defaults to "cyan". + info_color (str, optional): The color of the info level. Defaults to "green". + warning_color (str, optional): The color of the warning level. Defaults to "yellow". + error_color (str, optional): The color of the error level. Defaults to "red". + critical_color (str, optional): The color of the critical level. Defaults to "red". + colorlog_fmt_parameters (str, optional): The format of the log message. Defaults to "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s". Returns: None @@ -206,7 +207,7 @@ def error(self, message): f"[{self.__timestamp()}] > ERROR: | {self.__pad_message(str(message))}\n" ) - def critical(self, message, FILECODE, ERRCODE, FUNCODE): + def critical(self, message, FILECODE: str, ERRCODE: str, FUNCODE: str): """ Logs a critical message to the error log File. diff --git a/CODE/__wrapper__.py b/CODE/__wrapper__.py index e7067cb..d2ef06d 100644 --- a/CODE/__wrapper__.py +++ b/CODE/__wrapper__.py @@ -1,8 +1,11 @@ +# Optional wrapper that you can build manually, if you want to ignore the restrictions of python in the +# main Logicytics file. This wrapper is not compulsory, but it is recommended to use it to build the exe. + # Special wrapper that the exe Logicytics is made out of, not compulsory, just to ignore some restrictions of python. # If you modify please run this command: # pyinstaller --noconfirm --onefile --console --icon "C:\Users\Hp\Desktop\Logicytics\IMG\EXE.ico" "C:\Users\Hp\Desktop\Logicytics\CODE\__wrapper__.py" -# Assuming Logicytics is in the Desktop, and the paths are correct +# Assuming Logicytics is in the Desktop, and the paths are unchanged (You may need to replace Hp with your username). # Then rename from __wrapper__.exe to Logicytics.exe import subprocess @@ -12,12 +15,12 @@ FLAG = tuple(sys.argv[1:]) if len(FLAG) == 0: - subprocess.run(["python", "Logicytics.py"], shell=True) + subprocess.run(["python", "Logicytics.py"], shell=False) elif len(FLAG) == 2: flag1, flag2 = FLAG - subprocess.run(["python", "Logicytics.py", flag1, flag2], shell=True) + subprocess.run(["python", "Logicytics.py", flag1, flag2], shell=False) else: flag1 = sys.argv[1] - subprocess.run(["python", "Logicytics.py", flag1], shell=True) + subprocess.run(["python", "Logicytics.py", flag1], shell=False) diff --git a/CODE/_debug.py b/CODE/_debug.py index 3b091d6..f715ad2 100644 --- a/CODE/_debug.py +++ b/CODE/_debug.py @@ -1,196 +1,255 @@ from __future__ import annotations -from __lib_actions import * -from __lib_log import Log - import platform -import sys +import os.path import requests -from datetime import datetime - - -sys_internal_executables = [ - "psfile.exe", - "PsGetsid.exe", - "PsInfo.exe", - "pslist.exe", - "PsLoggedon.exe", - "psloglist.exe", -] - - -class SystemInfo: - def __init__(self): +import psutil +import sys +from __lib_class import * + +if __name__ == "__main__": + log_debug = Log(debug=DEBUG, filename="../ACCESS/LOGS/DEBUG/DEBUG.LOG") + log_debug_funcs = { + "INFO": log_debug.info, + "WARNING": log_debug.warning, + "ERROR": log_debug.error, + "CRITICAL": log_debug.critical, + None: log_debug.debug, + } + + +class HealthCheck: + def get_online_config( + self, + ) -> bool | tuple[tuple[str, str, str], tuple[str, str, str]]: """ - Initializes a new instance of the SystemInfo class. - - This constructor sets various system properties, including: - - device_model: The machine type of the device. - - python_version: The version of the Python interpreter. - - current_path: The absolute path of the current file. - - is_vm: A boolean indicating whether the environment is a virtual environment. - - is_admin: A boolean indicating whether the process is running with administrative privileges. - - execution_policy: The execution policy of the system. - - os_name: The name of the operating system. - - os_version: The version of the operating system. - - manufacturer: The processor manufacturer. - - Parameters: - None + Retrieves configuration data from a remote repository and compares it with the local configuration. Returns: - None + bool: False if a connection error occurs, otherwise a tuple containing version check and file check results. + tuple[tuple[str, str, str], tuple[str, str, str]]: A tuple containing version check and file check results. """ - self.device_model = platform.machine() - self.python_version = sys.version.split()[0] - self.current_path = os.path.dirname(os.path.abspath(__file__)) - self.is_vm = os.environ.get("VIRTUAL_ENV") is not None - self.is_admin = os.getpid() == 0 - self.execution_policy = ( - os.popen("powershell Get-ExecutionPolicy").read().strip() - ) - self.os_name = platform.system() - self.os_version = platform.release() - self.manufacturer = platform.processor() + try: + url = "https://raw.githubusercontent.com/DefinetlyNotAI/Logicytics/main/CODE/config.json" + config = json.loads(requests.get(url).text) + except requests.exceptions.ConnectionError: + log_debug.warning("No connection found") + return False + version_check = self.__compare_versions(VERSION, config["VERSION"]) + file_check = self.__check_files(CURRENT_FILES, config["CURRENT_FILES"]) + + return version_check, file_check @staticmethod - def get_date_time() -> str: + def __compare_versions( + local_version: str, remote_version: str + ) -> tuple[str, str, str]: """ - Get the current date and time in the format 'YYYY-MM-DD HH:MM:SS'. + Compares the local version with the remote version and returns a tuple containing a comparison result message, + a version information message, and a severity level. + + Args: + local_version (str): The version number of the local system. + remote_version (str): The version number of the remote repository. Returns: - str: The current date and time in the format 'YYYY-MM-DD HH:MM:SS'. + tuple[str, str, str]: A tuple containing a comparison result message, a version information message, and a severity level. """ - now = datetime.now() - return f"{now.strftime('%Y-%m-%d %H:%M:%S')}" + if local_version == remote_version: + return "Version is up to date.", f"Your Version: {local_version}", "INFO" + elif local_version > remote_version: + return ( + "Version is ahead of the repository.", + f"Your Version: {local_version}, Repository Version: {remote_version}", + "WARNING", + ) + else: + return ( + "Version is behind the repository.", + f"Your Version: {local_version}, Repository Version: {remote_version}", + "ERROR", + ) - @property - def is_admin(self) -> bool: + @staticmethod + def __check_files(local_files: list, remote_files: list) -> tuple[str, str, str]: """ - Check if the current process is running with administrative privileges. + Check if all the files in the local_files list are present in the remote_files list. + + Args: + local_files (list): A list of files in the local repository. + remote_files (list): A list of files in the remote repository. Returns: - bool: True if the process is running as an administrator, False otherwise. + tuple[str, str, str]: A tuple containing the result message, a message detailing the files present or missing, + and the log level. """ - return os.environ.get("PROCESSOR_ARCH") == "x86_64" + missing_files = set(remote_files) - set(local_files) + if not missing_files: + return ( + "All files are present.", + f"Your files: {local_files} contain all the files in the repository.", + "INFO", + ) + else: + return ( + "You have missing files.", + f"You are missing the following files: {missing_files}", + "ERROR", + ) + - @is_admin.setter - def is_admin(self, value: bool): +class DebugCheck: + @staticmethod + def SysInternal_Binaries(path: str) -> tuple[str, str]: """ - Sets the value of the is_admin property. + Checks the contents of the given path and determines the status of the SysInternal Binaries. - Parameters: - value (bool): The new value for the is_admin property. + Args: + path (str): The path to the directory containing the SysInternal Binaries. Returns: - None - """ - self._is_admin = value + tuple[str, str]: A tuple containing a status message and a severity level. + The status message indicates the result of the check. + The severity level is either "INFO", "WARNING", or "ERROR". + Raises: + FileNotFoundError: If the given path does not exist. + Exception: If an unexpected error occurs during the check. + """ + try: + contents = os.listdir(path) + log_debug.debug(contents) + if any(file.endswith(".ignore") for file in contents): + return "A `.sys.ignore` file was found - Ignoring", "WARNING" + if any(file.endswith(".zip") for file in contents) and not any( + file.endswith(".exe") for file in contents + ): + return "Only zip files - Missing EXE's due to no `ignore` file", "ERROR" + elif any(file.endswith(".zip") for file in contents) and any( + file.endswith(".exe") for file in contents + ): + return "Both zip and exe files - All good", "INFO" + else: + return ( + "SysInternal Binaries Not Found: Missing Files - Corruption detected", + "ERROR", + ) + except FileNotFoundError: + return ( + "SysInternal Binaries Not Found: Missing Directory- Corruption detected", + "ERROR", + ) + except Exception as e: + return f"An Unexpected error occurred: {e}", "ERROR" -class JSON: @staticmethod - def update_json_file(filename: str, new_array: list): + def execution_policy() -> bool: """ - Updates a JSON file with a new array of current files. - - Parameters: - filename (str): The path to the JSON file to update. - new_array (list): The new array of current files to write to the JSON file. + Checks the current PowerShell execution policy. Returns: - None + bool: True if the execution policy is unrestricted, False otherwise. """ - with open(filename, "r+") as f: - data = json.load(f) - data["CURRENT_FILES"] = new_array - f.seek(0) - json.dump(data, f, indent=4) - f.truncate() + result = subprocess.run( + ["powershell", "-Command", "Get-ExecutionPolicy"], + capture_output=True, + text=True, + ) + return result.stdout.strip().lower() == "unrestricted" @staticmethod - def get_json_data(URL: str) -> dict: + def cpu_info() -> tuple[str, str, str]: """ - Retrieves data from a specified URL and returns it as a dictionary. - - Parameters: - URL (str): The URL to retrieve data from. + Retrieves information about the CPU. Returns: - dict: A dictionary containing the retrieved data. + tuple[str, str, str]: A tuple containing the CPU architecture, vendor ID, and model. """ - response = requests.get(URL) - return response.json() + return ( + "CPU Architecture: " + platform.machine(), + "CPU Vendor Id: " + platform.system(), + "CPU Model: " + f"{platform.release()} {platform.version()}", + ) -def check_sys_internal_dir() -> tuple[bool, bool] | None: - if os.path.exists("SysInternal_Suite"): - return any( - os.path.exists(f"SysInternal_Suite/{file}") - for file in sys_internal_executables - ), os.path.exists("SysInternal_Suite/SysInternal_Suite.zip") - else: - return None +def debug(): + """ + Performs a series of system checks and logs the results. + + This function performs the following checks: + 1. Clears the debug log file. + 2. Checks the integrity of files by comparing local and remote configurations. + 3. Checks the status of SysInternal Binaries. + 4. Checks for admin privileges. + 5. Checks if User Account Control (UAC) is enabled. + 6. Logs the execution paths. + 7. Checks if the script is running in a virtual environment. + 8. Checks the PowerShell execution policy. + 9. Logs the Python version being used. + 10. Logs the repository path. + 11. Logs CPU information. + 12. Logs the debug configuration. + + Returns: + None + """ + # Clear Debug Log + if os.path.exists("../ACCESS/LOGS/DEBUG/DEBUG.LOG"): + os.remove("../ACCESS/LOGS/DEBUG/DEBUG.LOG") + + # Check File integrity (Online) + if HealthCheck().get_online_config(): + version_tuple, file_tuple = HealthCheck().get_online_config() + log_debug_funcs.get(version_tuple[2], log_debug.debug)( + "\n".join(version_tuple[0]).replace("\n", "") + ) + log_debug_funcs.get(file_tuple[2], log_debug.debug)( + "\n".join(file_tuple[0]).replace("\n", "") + ) + message, type = DebugCheck.SysInternal_Binaries("SysInternal_Suite") + log_debug_funcs.get(type, log_debug.debug)("\n".join(message).replace("\n", "")) + # Check Admin + if Check().admin(): + log_debug.info("Admin privileges found") + else: + log_debug.warning("Admin privileges not found") -def debug(): - # Set required variables - info = SystemInfo() - log = Log(debug=True, filename="../ACCESS/LOGS/DEBUG/Logicytics_Debug.log") - JSON.update_json_file("config.json", Actions.check_current_files(".")) - data = JSON.get_json_data( - "https://raw.githubusercontent.com/DefinetlyNotAI/Logicytics/main/CODE/config.json" - ) - Nx1, Nx2, N_VERSION, Nx3, N_CURRENT_FILES = Actions().read_config() - extra_in_config = set(N_CURRENT_FILES).difference(set(data["CURRENT_FILES"])) - missing_in_config = set(data["CURRENT_FILES"]).difference(set(N_CURRENT_FILES)) - diff = set(data["CURRENT_FILES"]).symmetric_difference(set(N_CURRENT_FILES)) - - # Create output - log.info( - info.device_model - + " " - + info.os_name - + " " - + info.os_version - + " " - + info.manufacturer - ) - log.info(info.python_version) - log.info(info.current_path) - log.info(f"Is VM: {info.is_vm}") - log.info(f"Running as admin: {info.is_admin}") - log.info(f"Execution policy: {info.execution_policy}") - log.info(f"Date and time: {info.get_date_time()}") - - # TODO Fix Me - """ - if diff != set(): - log.warning(f"Differences: {diff}") - if missing_in_config != set(): - log.error(f"Missing in your config.json: {missing_in_config}") - log.critical("Corruption Found", "_d", "C", "BA") - if extra_in_config != set(): - log.warning(f"Extra in your config.json: {extra_in_config}") - if len(diff) == 0 and len(missing_in_config) == 0 and len(extra_in_config) == 0: - log.info("Files are up-to date, No differences found") - if data["VERSION"] == N_VERSION: - log.info(f"Up to date: {VERSION}") - elif data["VERSION"] >= N_VERSION: - log.warning(f"Not up to date: {VERSION}") + # Check UAC + if Check().uac(): + log_debug.info("UAC enabled") else: - log.warning(f"Modified: {VERSION}") - """ + log_debug.warning("UAC disabled") - csid = check_sys_internal_dir() + # Check Execution Path + log_debug.info(f"Execution path: {psutil.__file__}") + log_debug.info(f"Global execution path: {sys.executable}") + log_debug.info(f"Local execution path: {sys.prefix}") - if isinstance(csid, tuple): - if csid[0]: - log.info("SysInternal Binaries Found: Unzipped") - elif csid[0] is False and csid[1]: - log.warning("SysInternal Binaries Not Found: Zipped") - else: - log.error("SysInternal Files Not found: Missing") + # Check if running in a virtual environment + if sys.prefix != sys.base_prefix: + log_debug.info("Running in a virtual environment") else: - log.error( - "SysInternal Directory Not found: Missing | Signifies minor corruption" - ) + log_debug.warning("Not running in a virtual environment") + + # Check Execution Policy + if DebugCheck.execution_policy(): + log_debug.info("Execution policy is unrestricted") + else: + log_debug.warning("Execution policy is not unrestricted") + + # Get Python Version + log_debug.info( + f"Python Version Used: {sys.version.split()[0]} - Recommended Version is: ~" + ) + + # Get Repo Path + log_debug.info(os.path.abspath(__file__).removesuffix("\\CODE\\_debug.py")) + + # Get CPU Info + architecture, vID, cpuModel = DebugCheck.cpu_info() + log_debug.info(architecture) + log_debug.info(vID) + log_debug.info(cpuModel) + + # Get config data + log_debug.info("Debug: " + DEBUG) diff --git a/CODE/_dev.py b/CODE/_dev.py index 13e436b..5e4d9d0 100644 --- a/CODE/_dev.py +++ b/CODE/_dev.py @@ -1,139 +1,156 @@ -import json -import os -import platform -import subprocess +from __future__ import annotations +from __lib_class import * -from __lib_actions import Actions -from __lib_log import Log - -def open_file(file: str) -> None: - """ - Opens a specified file using its default application in a cross-platform manner. - Args: - file (str): The path to the file to be opened. - Returns: - None - """ - if not file == "": - file_path = os.path.realpath(file) +class Dev: + @staticmethod + def __update_json_file(filename: str, new_data: list | str, key: str) -> None: + """ + Updates a JSON file with a new array of current files. + Args: + filename (str): The path to the JSON file to be updated. + new_data (list | str): The list of current files to be written to the JSON file. + key (str): The key in the JSON file to be updated. + Returns: + None + """ try: - if platform.system() == "Windows": - os.startfile(file_path) - elif platform.system() == "Darwin": # macOS - subprocess.call(("open", file_path)) - else: # Linux variants - subprocess.call(("xdg-open", file_path)) + with open(filename, "r+") as f: + data = json.load(f) + data[key] = new_data + f.seek(0) + json.dump(data, f, indent=4) + f.truncate() + except FileNotFoundError: + log_dev.error(f"File not found: {filename}") + except json.JSONDecodeError: + log_dev.error(f"Error decoding JSON in the file: {filename}") except Exception as e: - log.error(f"Error opening file: {e}") - - -def update_json_file(filename: str, new_array: list) -> None: - """ - Updates a JSON file with a new array of current files. - Args: - filename (str): The path to the JSON file to be updated. - new_array (list): The list of current files to be written to the JSON file. - Returns: - None - """ - try: - with open(filename, "r+") as f: - data = json.load(f) - data["CURRENT_FILES"] = new_array - f.seek(0) - json.dump(data, f, indent=4) - f.truncate() - except FileNotFoundError: - log.error(f"File not found: {filename}") - except json.JSONDecodeError: - log.error(f"Error decoding JSON in the file: {filename}") - except Exception as e: - log.error(f"An error occurred: {e}") + log_dev.error(f"An error occurred: {e}") + @staticmethod + def __prompt_user(question: str, file_to_open: str = None) -> bool: + """ + Prompts the user with a question and optionally opens a file if the answer is not 'yes'. + Args: + question (str): The question to ask the user. + file_to_open (str, optional): The file to open if the user doesn't answer 'yes'. + Returns: + bool: True if the user's answer is 'yes', otherwise False. + """ + try: + answer = input(question + " (yes or no):- ") + if answer.lower() != "yes": + if file_to_open: + Actions().open_file(file_to_open) + print( + "Please ensure you fix the issues/problem and try again with the checklist." + ) + return False + return True + except Exception as e: + log_dev.error(e) -def prompt_user(question: str, file_to_open: str = None) -> bool: - """ - Prompts the user with a question and optionally opens a file if the answer is not 'yes'. - Args: - question (str): The question to ask the user. - file_to_open (str, optional): The file to open if the user doesn't answer 'yes'. - Returns: - bool: True if the user's answer is 'yes', otherwise False. - """ - answer = input(question + " (yes or no):- ") - if answer.lower() != "yes": - if file_to_open: - open_file(file_to_open) - log.warning( - "Please ensure you fix the issues/problem and try again with the checklist." - ) - return False - return True + def __dev_checks(self) -> bool: + """ + Performs a series of checks to ensure that the developer has followed the required guidelines and best practices. + This function prompts the developer with a series of questions to ensure that they have followed the required + contributing guidelines, added files with a specific naming convention, added the file to the CODE directory, + added docstrings and comments to their code, tested their code, ensured that each file contains only one feature, + and has included the proper flags in their code. + Returns: + None + """ + checks = [ + ( + "Have you read the required contributing guidelines?", + "../CONTRIBUTING.md", + ), + ("Have you made files you don't want to be run start with '_'?", "."), + ("Have you added the file to CODE dir?", "."), + ("Have you added docstrings and comments?", "../CONTRIBUTING.md"), + ("Have you tested your code?", "../TESTS/TEST.py"), + ("Is each file containing no more than 1 feature?", "../CONTRIBUTING.md"), + ( + "Have you NOT modified __wrapper__.py without authorization?", + "Logicytics.py", + ), + ] + try: + for question, file_to_open in checks: + if not self.__prompt_user(question, file_to_open): + return False + remind = False + if self.__prompt_user( + "Is the update a major or minor upgrade (non-patch update)?" + ): + if not self.__prompt_user( + "Did You Build the EXE with Advanced Installer?", + "../Logicytics.aip", + ): + return False + else: + remind = True -def dev_checks() -> None: - """ - Performs a series of checks to ensure that the developer has followed the required guidelines and best practices. - This function prompts the developer with a series of questions to ensure that they have followed the required - contributing guidelines, added files with a specific naming convention, added the file to the CODE directory, - added docstrings and comments to their code, tested their code, ensured that each file contains only one feature, - and has included the proper flags in their code. - Returns: - None - """ - checks = [ - ("Have you read the required contributing guidelines?", "../CONTRIBUTING.md"), - ("Have you made files you don't want to be run start with '_'?", "."), - ("Have you added the file to CODE dir?", "."), - ("Have you added docstrings and comments?", "../CONTRIBUTING.md"), - ("Have you tested your code?", "../TESTS/TEST.py"), - ("Is each file containing no more than 1 feature?", "../CONTRIBUTING.md"), - ("Have you NOT modified __wrapper__.py without authorization?", "Logicytics.py"), - ] + files = Actions.check_current_files(".") + print(files) + if not self.__prompt_user("Does the list above include your added files?"): + log_dev.error("Something went wrong! Please contact support.") + return False - for question, file_to_open in checks: - if not prompt_user(question, file_to_open): - return + self.__update_json_file("config.json", files, "CURRENT_FILES") + self.__update_json_file( + "config.json", + input( + f"Enter the new version of the project (Old version is {VERSION}):" + ), + "VERSION", + ) + print( + "Great Job! Please tick the box in the GitHub PR request for completing steps in --dev" + ) + if remind: + print("Remember to upload the EXE files on the PR!") + return True + except Exception as e: + log_dev.error(e) + return False - remind = False - if prompt_user("Is the update a major or minor upgrade (non-patch update)?"): - if not prompt_user( - "Did You Build the EXE with Advanced Installer?", - "../Logicytics.aip", - ): - return - else: - remind = True + def run_dev(self): + """ + Executes the development checks and runs the test files. - files = Actions.check_current_files(".") - print(files) - if not prompt_user("Does the list above include your added files?"): - log.error("Something went wrong! Please contact support.") - return + This function performs the following steps: + 1. Creates necessary directories. + 2. Executes development checks to ensure guidelines and best practices are followed. + 3. Collects and runs all Python test files in the `../TESTS` directory, excluding `__init__.py` and `test.py`. - update_json_file("config.json", files) - log.info( - "Great Job! Please tick the box in the GitHub PR request for completing steps in --dev" - ) - if remind: - log.info("Remember to upload the EXE files on the PR!") + Returns: + None + """ + Actions().mkdir() + if self.__dev_checks(): + test_files = [] + for item in os.listdir("../TESTS"): + if ( + item.lower().endswith(".py") + and item.lower() != "__init__.py" + and item.lower() != "test.py" + ): + full_path = os.path.abspath(os.path.join("../TESTS", item)) + test_files.append(full_path) + log_dev.info(f"Found test file: {item} - Full path: {full_path}") + for item in test_files: + log_dev.info(Actions().run_command(f"python {item}")) -if __name__ == "__main__": - Actions().mkdir() - log = Log("../ACCESS/LOGS/DEV_TOOL.log", debug=True) - dev_checks() - log.info("Completed manual checks") - test_files = [] - for item in os.listdir("../TESTS"): - if ( - item.lower().endswith(".py") - and item.lower() != "__init__.py" - and item.lower() != "test.py" - ): - full_path = os.path.abspath(os.path.join("../TESTS", item)) - test_files.append(full_path) - log.debug(f"Found test file: {item} - Full path: {full_path}") - for item in test_files: - Actions().run_command(f"python {item}") +log_dev = Log(debug=DEBUG) +log_dev_funcs = { + "INFO": log_dev.info, + "WARNING": log_dev.warning, + "ERROR": log_dev.error, + "CRITICAL": log_dev.critical, + None: log_dev.debug, +} +Dev().run_dev() diff --git a/CODE/_extra.py b/CODE/_extra.py index c03fda0..d4fabf5 100644 --- a/CODE/_extra.py +++ b/CODE/_extra.py @@ -1,9 +1,17 @@ -import os -import subprocess -import zipfile +from __lib_class import * +if __name__ == "__main__": + log = Log(debug=DEBUG, filename="../ACCESS/LOGS/DEBUG/DEBUG.LOG") + log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, + } -def unzip(zip_path: str) -> None: + +def unzip(zip_path: str): """ Unzips a given zip file to a new directory with the same name. @@ -24,7 +32,7 @@ def unzip(zip_path: str) -> None: z.extractall(path=str(output_dir)) -def menu() -> None: +def menu(): """ Displays a menu of available executable scripts in the '../EXTRA/EXTRA' directory, prompts the user to select a script, and runs the selected script using PowerShell. @@ -38,18 +46,18 @@ def menu() -> None: for f in os.listdir("../EXTRA/EXTRA") if f.endswith(".exe") or f.endswith(".ps1") ] - print("Available scripts:") + log.info("Available scripts:") for i, file in enumerate(files): - print(f"{i+1}. {file}") + print(f"{i + 1}. {file}") except FileNotFoundError: - print( + log.error( "Error: ../EXTRA/EXTRA directory not found - Did you unzip it using --unzip-extra flag?" ) exit(1) choice = int(input("Enter the number of your chosen script: ")) if files[choice - 1] == "CMD.ps1": - print("Redirecting to CMD.ps1...") + log.info("Redirecting to CMD.ps1...") subprocess.run(["powershell.exe", "../EXTRA/EXTRA/CMD.ps1"], check=True) command = input("Type the flags you want to Execute: ") subprocess.run( diff --git a/CODE/_health.py b/CODE/_health.py index 504de59..d52bf90 100644 --- a/CODE/_health.py +++ b/CODE/_health.py @@ -1,7 +1,5 @@ -import os import shutil -import subprocess -import zipfile +from __lib_class import * def backup(directory: str, name: str) -> None: @@ -16,8 +14,8 @@ def backup(directory: str, name: str) -> None: None """ # Check if backup exists, delete it if so - if os.path.exists("../Access/Backup/backup.zip"): - os.remove("../Access/Backup/backup.zip") + if os.path.exists(f"../ACCESS/BACKUP/{name}.zip"): + os.remove(f"../ACCESS/BACKUP/{name}.zip") # Zip the directory and move it to the backup location with zipfile.ZipFile(f"{name}.zip", "w") as zip_file: @@ -27,10 +25,10 @@ def backup(directory: str, name: str) -> None: relative_path = os.path.relpath(str(file_path), start=os.getcwd()) zip_file.write(str(file_path), arcname=relative_path) - shutil.move("backup.zip", "../Access/Backup") + shutil.move(f"{name}.zip", "../ACCESS/BACKUP") -def update() -> None: +def update() -> str: """ Updates the repository by pulling the latest changes from the remote repository. @@ -43,5 +41,6 @@ def update() -> None: current_dir = os.getcwd() parent_dir = os.path.dirname(current_dir) os.chdir(parent_dir) - subprocess.run(["git", "pull"]) + output = subprocess.run(["git", "pull"]).stdout.decode() os.chdir(current_dir) + return output diff --git a/CODE/_hide_my_tracks.py b/CODE/_hide_my_tracks.py index 0da4a7a..5b3abb8 100644 --- a/CODE/_hide_my_tracks.py +++ b/CODE/_hide_my_tracks.py @@ -18,4 +18,4 @@ def attempt_hide(): for file in os.listdir(log_path): if file.endswith(".evtx") and file.startswith(today.strftime("%Y-%m-%d")): - subprocess.run(f'del "{os.path.join(log_path, file)}"', shell=True) + subprocess.run(f'del "{os.path.join(log_path, file)}"', shell=False) diff --git a/CODE/_zipper.py b/CODE/_zipper.py index 59c6364..e967d27 100644 --- a/CODE/_zipper.py +++ b/CODE/_zipper.py @@ -1,75 +1,161 @@ +from __future__ import annotations import hashlib -import os import shutil +from datetime import datetime +import os import zipfile -from datetime import date - - -def get_files_to_zip(path: str) -> list: - return [ - f - for f in os.listdir(path) - if not f.endswith((".py", ".exe", ".bat", ".ps1")) - and not f.startswith(("config.", "SysInternal_Suite", "__pycache__")) - ] -def create_zip_file(path: str, files: list, filename: str): - with zipfile.ZipFile(f"{filename}.zip", "w") as zip_file: +class Zip: + """ + A class to handle zipping files, generating SHA256 hashes, and moving files. + + Methods: + __get_files_to_zip(path: str) -> list: + Returns a list of files to be zipped, excluding certain file types and names. + + __create_zip_file(path: str, files: list, filename: str): + Creates a zip file from the given list of files. + + __remove_files(path: str, files: list): + Removes the specified files from the given path. + + __generate_sha256_hash(filename: str) -> str: + Generates a SHA256 hash for the specified zip file. + + __write_hash_to_file(filename: str, sha256_hash: str): + Writes the SHA256 hash to a file. + + __move_files(filename: str): + Moves the zip file and its hash file to designated directories. + + and_hash(self, path: str, name: str, flag: str) -> tuple | str: + Zips files, generates a SHA256 hash, and moves the files. + """ + + @staticmethod + def __get_files_to_zip(path: str) -> list: + """ + Returns a list of files to be zipped, excluding certain file types and names. + + Args: + path (str): The directory path to search for files. + + Returns: + list: A list of file names to be zipped. + """ + return [ + f + for f in os.listdir(path) + if not f.endswith((".py", ".exe", ".bat", ".ps1")) + and not f.startswith(("config.", "SysInternal_Suite", "__pycache__")) + ] + + @staticmethod + def __create_zip_file(path: str, files: list, filename: str): + """ + Creates a zip file from the given list of files. + + Args: + path (str): The directory path containing the files. + files (list): A list of file names to be zipped. + filename (str): The name of the output zip file. + + Returns: + None + """ + with zipfile.ZipFile(f"{filename}.zip", "w") as zip_file: + for file in files: + zip_file.write(os.path.join(path, file)) + + @staticmethod + def __remove_files(path: str, files: list): + """ + Removes the specified files from the given path. + + Args: + path (str): The directory path containing the files. + files (list): A list of file names to be removed. + + Returns: + None or str: Returns an error message if an exception occurs. + """ for file in files: - zip_file.write(os.path.join(path, file)) - - -def remove_files(path: str, files: list): - for file in files: - try: - shutil.rmtree(os.path.join(path, file)) - except OSError: - os.remove(os.path.join(path, file)) - except Exception as e: - print(e) - - -def generate_sha256_hash(filename: str) -> str: - with open(f"{filename}.zip", "rb") as zip_file: - zip_data = zip_file.read() - return hashlib.sha256(zip_data).hexdigest() - - -def write_hash_to_file(filename: str, sha256_hash: str): - with open(f"{filename}.hash", "w") as hash_file: - hash_file.write(sha256_hash) - - -def move_files(filename: str): - shutil.move(f"{filename}.zip", "../ACCESS/DATA/Zip") - shutil.move(f"{filename}.hash", "../ACCESS/DATA/Hashes") - - -def check_and_delete_files_with_suffix(directory: str, suffix: str): - deleted_files = [] - for filename in os.listdir(directory): - if filename.endswith(suffix): - file_path = os.path.join(directory, filename) - os.remove(file_path) - deleted_files.append(file_path) - return deleted_files - - -def zip_and_hash(path: str, name: str, action: str) -> tuple: - deleted_files_hash = check_and_delete_files_with_suffix('../ACCESS/DATA/Hashes', '.hash') - deleted_files_zip = check_and_delete_files_with_suffix('../ACCESS/DATA/Zip', '.zip') - today = date.today() - filename = f"Logicytics_{name}_{action}_{today.strftime('%Y-%m-%d')}" - files_to_zip = get_files_to_zip(path) - create_zip_file(path, files_to_zip, filename) - remove_files(path, files_to_zip) - sha256_hash = generate_sha256_hash(filename) - write_hash_to_file(filename, sha256_hash) - move_files(filename) - return ( - f"Zip file moved to ../ACCESS/DATA/Zip/{filename}.zip", - f"SHA256 Hash file moved to ../ACCESS/DATA/Hashes/{filename}.hash", - deleted_files_zip, - deleted_files_hash - ) + try: + shutil.rmtree(os.path.join(path, file)) + except OSError: + os.remove(os.path.join(path, file)) + except Exception as e: + return f"Error: {e}" + + @staticmethod + def __generate_sha256_hash(filename: str) -> str: + """ + Generates a SHA256 hash for the specified zip file. + + Args: + filename (str): The name of the zip file. + + Returns: + str: The SHA256 hash of the zip file. + """ + with open(f"{filename}.zip", "rb") as zip_file: + zip_data = zip_file.read() + return hashlib.sha256(zip_data).hexdigest() + + @staticmethod + def __write_hash_to_file(filename: str, sha256_hash: str): + """ + Writes the SHA256 hash to a file. + + Args: + filename (str): The name of the hash file. + sha256_hash (str): The SHA256 hash to be written. + + Returns: + None + """ + with open(f"{filename}.hash", "w") as hash_file: + hash_file.write(sha256_hash) + + @staticmethod + def __move_files(filename: str): + """ + Moves the zip file and its hash file to designated directories. + + Args: + filename (str): The name of the files to be moved. + + Returns: + None + """ + shutil.move(f"{filename}.zip", "../ACCESS/DATA/Zip") + shutil.move(f"{filename}.hash", "../ACCESS/DATA/Hashes") + + def and_hash(self, path: str, name: str, flag: str) -> tuple | str: + """ + Zips files, generates a SHA256 hash, and moves the files. + + Args: + path (str): The directory path containing the files. + name (str): The base name for the output files. + flag (str): A flag to be included in the output file names. + + Returns: + tuple or str: A tuple containing success messages or an error message. + """ + today = datetime.now() + filename = f"Logicytics_{name}_{flag}_{today.strftime('%Y-%m-%d_%H-%M-%S')}" + files_to_zip = self.__get_files_to_zip(path) + self.__create_zip_file(path, files_to_zip, filename) + check = self.__remove_files(path, files_to_zip) + if isinstance(check, str): + return check + else: + sha256_hash = self.__generate_sha256_hash(filename) + self.__write_hash_to_file(filename, sha256_hash) + self.__move_files(filename) + return ( + f"Zip file moved to ../ACCESS/DATA/Zip/{filename}.zip", + f"SHA256 Hash file moved to ../ACCESS/DATA/Hashes/{filename}.hash", + ) diff --git a/CODE/browser_miner.ps1 b/CODE/browser_miner.ps1 index f6a8fd8..bf31d5d 100644 --- a/CODE/browser_miner.ps1 +++ b/CODE/browser_miner.ps1 @@ -47,7 +47,7 @@ $fullSourcePath = $sourcePath -replace '\{\}', $currentUser # Enhanced error checking for source path existence and accessibility if (-not (Test-PathAndAccess $fullSourcePath)) { - Write-Host "Source path $fullSourcePath does not exist or cannot be accessed." + Write-Host "ERROR: Source path $fullSourcePath does not exist or cannot be accessed." continue } @@ -60,7 +60,7 @@ $identifier = $identifiers[$index] } catch { - Write-Host "Failed to extract identifier from source path $fullSourcePath." + Write-Host "ERROR: Failed to extract identifier from source path $fullSourcePath." continue } @@ -79,13 +79,12 @@ try { Copy-Item -Path $fullSourcePath -Destination $destinationPath -Recurse -Force -ErrorAction SilentlyContinue # Print the success message to the console -Write-Host "Successfully copied $fullSourcePath to $destinationPath" +Write-Host "INFO: Successfully copied $fullSourcePath to $destinationPath" } catch { # Detailed error handling - Write-Host "An error occurred while copying $fullSourcePath to $destinationPath. Error: $_" -Invoke-CrashReport -ErrorId "OGE" -FunctionNo "fun93" -ErrorContent $_ -Type "crash" + Write-Host "ERROR: An error occurred while copying $fullSourcePath to $destinationPath. Error: $_" exit } } \ No newline at end of file diff --git a/CODE/config.json b/CODE/config.json index c8722ef..44431de 100644 --- a/CODE/config.json +++ b/CODE/config.json @@ -2,8 +2,33 @@ "WEBHOOK URL": "IGNORE ME FOR NOW", "ipgeolocation.io API KEY": "OPTIONAL - ADD A KEY", "DEBUG": false, - "VERSION": "2.2.0", + "VERSION": "2.3.0", "CURRENT_FILES": [ - "WIP - BROKEN" + "browser_miner.ps1", + "driverquery+sysinfo.py", + "Logicytics.py", + "log_miner.py", + "media_backup.py", + "netadapter.ps1", + "online_ip_scraper.py", + "property_scraper.ps1", + "registry.py", + "sensitive_data_miner.py", + "ssh_miner.py", + "sys_internal.py", + "tasklist.py", + "tree.bat", + "wifi_stealer.py", + "window_feature_miner.ps1", + "wmic.py", + "_debug.py", + "_dev.py", + "_extra.py", + "_health.py", + "_hide_my_tracks.py", + "_zipper.py", + "__lib_class.py", + "__lib_log.py", + "__wrapper__.py" ] } \ No newline at end of file diff --git a/CODE/driverquery+sysinfo.py b/CODE/driverquery+sysinfo.py index 5344da7..09f7ad6 100644 --- a/CODE/driverquery+sysinfo.py +++ b/CODE/driverquery+sysinfo.py @@ -1,8 +1,27 @@ -from __lib_actions import * -from __lib_log import Log +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} def command(file: str, com: str, message: str): + """ + Executes a command and writes the output to a file. + + Args: + file (str): The name of the file to write the command output to. + com (str): The command to be executed. + message (str): A message to be logged. + + Returns: + None + """ try: output = Actions.run_command(com) open(file, "w").write(output) @@ -12,6 +31,5 @@ def command(file: str, com: str, message: str): log.info(f"{message} Executed") -log = Log(debug=DEBUG) command("Drivers.txt", "driverquery /v", "Driver Query") command("SysInfo.txt", "systeminfo", "System Info") diff --git a/CODE/log_miner.py b/CODE/log_miner.py index fe60edf..2c766ee 100644 --- a/CODE/log_miner.py +++ b/CODE/log_miner.py @@ -1,5 +1,13 @@ -from __lib_actions import * -from __lib_log import * +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} def backup_windows_logs(): @@ -39,5 +47,4 @@ def backup_windows_logs(): log.info("Log Miner completed.") -log = Log(debug=DEBUG) backup_windows_logs() diff --git a/CODE/media_backup.py b/CODE/media_backup.py index eaa4f29..d7d91ec 100644 --- a/CODE/media_backup.py +++ b/CODE/media_backup.py @@ -1,61 +1,68 @@ import getpass import shutil from datetime import datetime +from __lib_class import * -from __lib_actions import * -from __lib_log import Log - - -def get_default_paths(): - """Returns the default paths for photos and videos based on the Windows username.""" - username = getpass.getuser() - default_photo_path = os.path.expanduser(f"C:\\Users\\{username}\\Pictures") - default_video_path = os.path.expanduser(f"C:\\Users\\{username}\\Videos") - return [default_photo_path, default_video_path] - - -def ensure_backup_directory_exists(backup_directory): - """Ensures the backup directory exists; creates it if not.""" - if not os.path.exists(backup_directory): - os.makedirs(backup_directory) - - -def collect_media_files(source_dirs): - """Collects all media files from the source directories.""" - media_files = [] - for source_dir in source_dirs: - for root, _, files in os.walk(source_dir): - for file in files: - if file.endswith((".jpg", ".jpeg", ".png", ".mp4")): - media_files.append(os.path.join(root, file)) - return media_files - - -def backup_files(media_files, backup_directory): - """Backs up media files to the backup directory.""" - for src_file in media_files: - dst_file = os.path.join( - backup_directory, - datetime.now().strftime("%Y-%m-%d_%H-%M-%S") - + "_" - + os.path.basename(src_file), - ) - try: - shutil.copy2(str(src_file), str(dst_file)) - log.info(f"Copied {os.path.basename(src_file)} to {dst_file}") - except Exception as e: - log.error(f"Failed to copy {src_file}: {str(e)}") - - -def backup_media(): - """Backs up media files from the default Windows photo and video directories.""" - source_dirs = get_default_paths() - backup_directory = "MediaBackup" - ensure_backup_directory_exists(backup_directory) - media_files = collect_media_files(source_dirs) - backup_files(media_files, backup_directory) - log.info("Media backup script completed.") +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} -log = Log(debug=DEBUG) -backup_media() +class Media: + @staticmethod + def __get_default_paths() -> list: + """Returns the default paths for photos and videos based on the Windows username.""" + username = getpass.getuser() + default_photo_path = os.path.expanduser(f"C:\\Users\\{username}\\Pictures") + default_video_path = os.path.expanduser(f"C:\\Users\\{username}\\Videos") + return [default_photo_path, default_video_path] + + @staticmethod + def __ensure_backup_directory_exists(backup_directory: str): + """Ensures the backup directory exists; creates it if not.""" + if not os.path.exists(backup_directory): + os.makedirs(backup_directory) + + @staticmethod + def __collect_media_files(source_dirs: list) -> list: + """Collects all media files from the source directories.""" + media_files = [] + for source_dir in source_dirs: + for root, _, files in os.walk(source_dir): + for file in files: + if file.endswith((".jpg", ".jpeg", ".png", ".mp4")): + media_files.append(os.path.join(root, file)) + return media_files + + @staticmethod + def __backup_files(media_files: list, backup_directory: str): + """Backs up media files to the backup directory.""" + for src_file in media_files: + dst_file = os.path.join( + backup_directory, + datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + + "_" + + os.path.basename(src_file), + ) + try: + shutil.copy2(str(src_file), str(dst_file)) + log.info(f"Copied {os.path.basename(src_file)} to {dst_file}") + except Exception as e: + log.error(f"Failed to copy {src_file}: {str(e)}") + + def backup(self): + """Backs up media files from the default Windows photo and video directories.""" + source_dirs = self.__get_default_paths() + backup_directory = "MediaBackup" + self.__ensure_backup_directory_exists(backup_directory) + media_files = self.__collect_media_files(source_dirs) + self.__backup_files(media_files, backup_directory) + log.info("Media backup script completed.") + + +Media().backup() diff --git a/CODE/netadapter.ps1 b/CODE/netadapter.ps1 index 0d01a66..9453ace 100644 --- a/CODE/netadapter.ps1 +++ b/CODE/netadapter.ps1 @@ -1,2 +1,3 @@ # Get all network details +Write-Output "INFO: Getting NetAdapter Info" Get-NetAdapter | Select-Object Name, Status, MacAddress, ifIndex, InterfaceAlias, InterfaceDescription | Out-File -FilePath .\Network.txt diff --git a/CODE/online_ip_scraper.py b/CODE/online_ip_scraper.py index 4cec21f..912fcda 100644 --- a/CODE/online_ip_scraper.py +++ b/CODE/online_ip_scraper.py @@ -1,11 +1,21 @@ +from __future__ import annotations + import requests -from __lib_actions import * -from __lib_log import Log +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} -class IP: +class Scrape: @staticmethod - def __get_my_ip(): + def __get_my_ip() -> str: """ Retrieves the user's current IP address from the ipify API. @@ -20,7 +30,7 @@ def __get_my_ip(): log.error(f"Failed to get IP: {e}") @staticmethod - def __get_location_data(ip, api_key=None): + def __get_location_data(ip: str, api_key=None) -> dict | None: """ Retrieves location data for a given IP address. @@ -42,7 +52,7 @@ def __get_location_data(ip, api_key=None): except Exception as e: log.error(f"Failed to get location data: {e}") - def scraper(self): + def ip(self): """ Scrapes the user's current IP address and its corresponding location data. @@ -59,5 +69,4 @@ def scraper(self): log.info("IP Scraper Executed") -log = Log(debug=DEBUG) -IP().scraper() +Scrape().ip() diff --git a/CODE/property_scraper.ps1 b/CODE/property_scraper.ps1 index fe29f7d..ce8ab6f 100644 --- a/CODE/property_scraper.ps1 +++ b/CODE/property_scraper.ps1 @@ -31,4 +31,4 @@ Property(C): Root Drive = $rootDrive $data | Out-File -FilePath ".\Extra_Data.txt" # Optionally, display a message indicating success -Write-Host "Data successfully written to Extra_Data.txt" +Write-Host "INFO: Data successfully written to Extra_Data.txt" diff --git a/CODE/registry.py b/CODE/registry.py index 9f3f8e3..c3f917f 100644 --- a/CODE/registry.py +++ b/CODE/registry.py @@ -1,5 +1,13 @@ -from __lib_actions import * -from __lib_log import * +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} def backup_registry(): @@ -22,12 +30,11 @@ def backup_registry(): try: # Execute the command - subprocess.run(cmd, shell=True, check=True) + subprocess.run(cmd, shell=False, check=True) log.info(f"Registry backed up successfully to {export_path}") except subprocess.CalledProcessError as e: log.error(f"Failed to back up the registry: {e}") log.info(f"Registry back-up executed") -log = Log(debug=DEBUG) backup_registry() diff --git a/CODE/sensitive_data_miner.py b/CODE/sensitive_data_miner.py index bf32ae6..04bb98c 100644 --- a/CODE/sensitive_data_miner.py +++ b/CODE/sensitive_data_miner.py @@ -1,8 +1,15 @@ import shutil from pathlib import Path +from __lib_class import * -from __lib_actions import * -from __lib_log import * +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} # List of allowed extensions allowed_extensions = [ @@ -18,12 +25,15 @@ ".text", ".docx", ".doc", + ".xls", + ".xlsx", + ".csv", ] -class Miner: +class Mine: @staticmethod - def __search_files_by_keyword(root, keyword): + def __search_files_by_keyword(root: Path, keyword: str) -> list: """ Searches for files containing the specified keyword in their names. Args: @@ -44,7 +54,7 @@ def __search_files_by_keyword(root, keyword): return matching_files @staticmethod - def __copy_file(src_file_path, dst_file_path): + def __copy_file(src_file_path: Path, dst_file_path: Path): """ Copies a file to the destination directory. Args: @@ -61,7 +71,7 @@ def __copy_file(src_file_path, dst_file_path): except Exception as e: log.error(f"Failed to copy file: {e}") - def __search_and_copy_files(self, keyword): + def __search_and_copy_files(self, keyword: str): """ Searches for files containing the specified keyword in their names and copies them to a destination directory. Args: @@ -100,5 +110,4 @@ def passwords(self): log.info("Sensitive Data Miner Completed") -log = Log(debug=DEBUG) -Miner().passwords() +Mine().passwords() diff --git a/CODE/ssh_miner.py b/CODE/ssh_miner.py index 43b333c..0198a65 100644 --- a/CODE/ssh_miner.py +++ b/CODE/ssh_miner.py @@ -1,6 +1,14 @@ -from __lib_log import * import shutil -from __lib_actions import * +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} def ssh_miner(): @@ -41,5 +49,4 @@ def ssh_miner(): log.info("SSH Miner completed.") -log = Log(debug=DEBUG) ssh_miner() diff --git a/CODE/sys_internal.py b/CODE/sys_internal.py index 1a1dda7..ddfb3ea 100644 --- a/CODE/sys_internal.py +++ b/CODE/sys_internal.py @@ -1,5 +1,13 @@ -from __lib_actions import * -from __lib_log import Log +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} sys_internal_executables = [ "psfile.exe", @@ -66,7 +74,6 @@ def check_sys_internal_dir() -> tuple[bool, bool]: return False, False -log = Log(debug=DEBUG) if check_sys_internal_dir()[0]: sys_internal() elif check_sys_internal_dir()[0] is False and check_sys_internal_dir()[1] is True: diff --git a/CODE/tasklist.py b/CODE/tasklist.py index 84d2982..613f0f8 100644 --- a/CODE/tasklist.py +++ b/CODE/tasklist.py @@ -1,5 +1,13 @@ -from __lib_actions import * -from __lib_log import Log +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} def tasklist(): @@ -27,5 +35,4 @@ def tasklist(): log.error(f"Error: {e}") -log = Log(debug=DEBUG) tasklist() diff --git a/CODE/todo b/CODE/todo new file mode 100644 index 0000000..2a38b2d --- /dev/null +++ b/CODE/todo @@ -0,0 +1,3 @@ +--dev +Then update readme, wiki and contributing guidelines +Finally after all, create a pr, and a release, then the release should contain the commit history etc \ No newline at end of file diff --git a/CODE/tree.bat b/CODE/tree.bat index 96d5f35..45dc8af 100644 --- a/CODE/tree.bat +++ b/CODE/tree.bat @@ -8,4 +8,6 @@ set "outputFile=Tree.txt" :: Run the tree command and redirect the output to the file powershell.exe -Command "& {tree /f C:\ | Out-File -FilePath '!outputFile!' -Force}" +echo INFO: Saved !outputFile! + endlocal diff --git a/CODE/wifi_stealer.py b/CODE/wifi_stealer.py new file mode 100644 index 0000000..61f5546 --- /dev/null +++ b/CODE/wifi_stealer.py @@ -0,0 +1,71 @@ +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} + + +def get_password(ssid: str) -> str or None: + """ + Retrieves the password associated with a given Wi-Fi SSID. + + Args: + ssid (str): The SSID of the Wi-Fi network. + + Returns: + str or None: The password associated with the SSID, or None if no password is found. + + Raises: + Exception: If an error occurs while executing the command. + """ + try: + command_output = Actions().run_command( + f'netsh wlan show profile name="{ssid}" key=clear' + ) + key_content = command_output.splitlines() + for line in key_content: + if "Key Content" in line: + return line.split(":")[1].strip() + return None + except Exception as e: + log.error(e) + + +def get_wifi_names() -> list: + """ + Retrieves the names of all Wi-Fi profiles on the system. + + This function executes the command "netsh wlan show profile" to retrieve the list of Wi-Fi profiles. + It then iterates over each line of the command output and checks if the line contains the string "All User Profile". + If it does, it extrActions()s the Wi-Fi profile name and appends it to the list of Wi-Fi names. + + Returns: + list: A list of Wi-Fi profile names. + """ + try: + log.info("Retrieving Wi-Fi names...") + command_output = Actions().run_command("netsh wlan show profile") + wifi_names = [] + + for line in command_output.split("\n"): + if "All User Profile" in line: + start_index = line.find("All User Profile") + len("All User Profile") + wifi_name = line[start_index:].strip() + wifi_names.append(wifi_name) + log.info(f"Retrieved {len(wifi_names)} Wi-Fi names.") + return wifi_names + except Exception as e: + log.error(e) + + +with open("WiFi.txt", "w") as file: + for name in get_wifi_names(): + log.info(f"Retrieving password for {name.removeprefix(': ')}") + file.write( + f"Name: {name.removeprefix(': ')}, Password: {get_password(name.removeprefix(': '))}\n" + ) diff --git a/CODE/window_feature_miner.ps1 b/CODE/window_feature_miner.ps1 index bc6ee9b..83832b7 100644 --- a/CODE/window_feature_miner.ps1 +++ b/CODE/window_feature_miner.ps1 @@ -1,2 +1,3 @@ # List all optional features and save them to Features.txt +Write-Output "INFO: Starting Feature mining, saving to Features.txt" Get-WindowsOptionalFeature -Online | Format-Table -Property FeatureName, State > Features.txt diff --git a/CODE/wmic.py b/CODE/wmic.py index 9a7be0c..6529700 100644 --- a/CODE/wmic.py +++ b/CODE/wmic.py @@ -1,5 +1,13 @@ -from __lib_actions import * -from __lib_log import Log +from __lib_class import * + +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} def wmic(): @@ -35,5 +43,4 @@ def wmic(): file.write("-" * 190) -log = Log(debug=DEBUG) wmic() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd38cf0..839b45f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,6 +65,16 @@ please adhere to the following guidelines to ensure consistency and maintainabil - If it's a file to be run, shouldn't start with `_` - If it's a extra file/extra library, to make sure it isn't run, should start with `_` - No code is allowed to have `if __name__ == '__main__'` or a similar functioning code +- You must start with the following code if using python: + +```python +from __lib_class import * # This imports everything needed including the unique logger called by log + +# Your actual code, must be able to run without any interference by outside actions +# USE log.info, log.error, log.warning and log.debug as well +# You can choose to use any other of the code without issues + +``` ## Issues and labels 🛠️ diff --git a/CREDITS.md b/CREDITS.md index dd18011..cb7d0b0 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -3,16 +3,24 @@ This project is built on the shoulders of giants and inspired by the work of many talented individuals and organizations. We acknowledge their contributions and are grateful for the knowledge and tools they have shared. + ## 👨‍💻 Coders Credits 👨‍💻 ### WEB directory & index.html by iamthgeawsomboi2099 -Created the Website of Logicytics +Created the Website of [Logicytics](index.html) as well as maintained it and fixed security headers +The sole creator of WEB directory - [iamthgeawsomboi2099](https://github.com/iamthgeawsomboi2099) - + +### Wifi-Stealer.py by ski-sketch +Created Wi-Fi Password Stealer using python +The sole creator of the code of wifi-stealer +- [ski-sketch](https://github.com/ski-sketch) + ## 🛠️ Refactorers Credits 🛠️ Until Now, no one. Become a contributor and help us spread the word. @@ -23,7 +31,9 @@ Until Now, no one. Become a contributor and help us spread the word. ## 🐛 Bug bounty credits 🐛 -Until Now, no one. Become a contributor and help us spread the word. +### Found development bug +Found and attempted fix of 2 bugs: Zipping name error - `--dev` flag loop +- [ski-sketch](https://github.com/ski-sketch) ## 🏢 Third Party Credits 🏢 diff --git a/MODS/IGNORE ME.txt b/MODS/IGNORE ME.txt deleted file mode 100644 index 08e1cab..0000000 --- a/MODS/IGNORE ME.txt +++ /dev/null @@ -1 +0,0 @@ -Placed cause GitHub doesn't allow empty directories, \ No newline at end of file diff --git a/MODS/_MOD_SKELETON.py b/MODS/_MOD_SKELETON.py new file mode 100644 index 0000000..b50eeb3 --- /dev/null +++ b/MODS/_MOD_SKELETON.py @@ -0,0 +1,42 @@ +# If using the future annotations, it should be ontop of the file +# from __future__ import annotations + +# Other Imports if needed or necessary go here + +# This imports everything needed including the unique logger called by log - It is not optional +# To know more check the WiKi [Section 2, Coding Rules and Tips, Custom Libraries, __lib_class.py] +from __lib_class import * +log = Log(debug=DEBUG) +log_funcs = { + "INFO": log.info, + "WARNING": log.warning, + "ERROR": log.error, + "CRITICAL": log.critical, + None: log.debug, +} + +# Your actual code, must be able to run without any interference by outside actions +# USE log.info, log.error, log.warning and log.debug as well +# You can choose to use any other of the code without issues +# Example of said code:- +# +# def MOD_EXAMPLE() -> None: +# """ +# This function MOD is used to log different types of messages. +# +# It logs an error message, a warning message, an info message, and a debug message. +# +# Parameters: +# None +# +# Returns: +# None +# """ +# log.error("This is an error") +# log.warning("This is a warning") +# log.info("This is a info message") +# log.debug("This is a debug message") +# pass # Your code here with proper logging +# +# +# MOD_EXAMPLE() diff --git a/README.md b/README.md index af894c4..d66a8ef 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ This comprehensive guide is here to equip you with everything you need to use Lo GitHub Branch Check Runs GitHub Repo Size -
GitHub Repo CodeFactor Rating GitHub Repo CodeClimate Rating @@ -30,8 +29,6 @@ Supporters will be placed in the Credits ❤️ ## 🛠️ Installation and Setup 🛠️ -### Installation and Setup - To install and setup Logicytics, follow these steps: 1. **Install Python**: If you don't have Python installed, you can download it from the [official website](https://www.python.org/downloads/). @@ -54,9 +51,9 @@ To install and setup Logicytics, follow these steps: - **Knowledge of Command Line**: The project uses command line options for the user to interact with the program. It is recommended to have a basic understanding of command line options. -- **Optional**: Create a `.ignore` file in the `CODE/SysInternal_Suite` directory to not extract the exe binaries from the ZIP file (This is done for the OpenSSF score), if the `.ignore` file is not found, it will auto extract the details +- **Optional**: Create a `.sys.ignore` file in the `CODE/SysInternal_Suite` directory to not extract the exe binaries from the ZIP file (This is done for the OpenSSF score), if the `.sys.ignore` file is not found, it will auto extract the details -## Step-by-Step Installation +## Step-by-Step Installation and Usage 1) Install Python 🐍 If you don't have Python installed, you can download it from the official website. @@ -127,8 +124,6 @@ the API key for ipgeolocation.io, the DEBUG flag, the VERSION, and the CURRENT_F CURRENT_FILES is an array of strings that contains the names of the files you have, this is used to later check for corruption or bugs. -Do not modify CURRENT_FILES and VERSION unless you are developing extra add-ons - ## 🚀 Advanced Usage 🚀 ![Main 3 Directories](IMG/3dir.png "The 3 Main Directories") @@ -185,24 +180,28 @@ If those don't work attempt: ### Support Resources Check out the [GitHub wiki](https://github.com/DefinetlyNotAI/Logicytics/wiki) for help -Check out the [python documentation](https://docs.python.org/3/) for help -Check out the [python subreddit](https://www.reddit.com/r/Python/) for help -Check out the [python stackexchange](https://stackoverflow.com/questions/tagged/python) for help when developing mods ## 📊 Data Analysis 📊 ## Data Extraction -Dont recreate these as they are already done: + +Logicytics extracts a wide range of data points on a Windows system. + +Here are some of the data points that Logicytics extracts: + +Don't recreate these: + - + + @@ -210,11 +209,12 @@ Dont recreate these as they are already done: - + + @@ -222,11 +222,12 @@ Dont recreate these as they are already done: - + + - + @@ -234,7 +235,8 @@ Dont recreate these as they are already done: - + + @@ -260,9 +262,30 @@ Dont recreate these as they are already done: + + + +
File Name AboutImportant Note
browser_miner.ps1Mines all data related to browsers [Would love to be updated]Mines all data related to browsersWould love to be updated
driverquery+sysinfo.py
log_miner.pyGets all logs from the windows deviceGets all logs from the Windows device
media_backup.py Gets all media of the device in a neat folderWould love to be updated
netadapter.ps1
online_ip_scraper.pyUses https://api.ipify.org to get your IP [May not be of much use]Uses https://api.ipify.org to get your IPMay not be of much use
property_scraper.ps1Gets all of the windows propertiesGets all the windows properties
registry.py
sensitive_data_miner.pyCopies all files that can be considered sensitive in a neat folder [Must be worked on - Super Janky]Copies all files that can be considered sensitive in a neat folderMust be worked on - Pretty inefficient
ssh_miner.pywmic.py Logs and runs many wmic commands to gain sensitive data and information
wifi_stealer.pyGets the SSID and Password of all saved Wi-Fi
-**Any file with `_` is not counted here** +This is not an exhaustive list, +but it should give you a good idea of what data Logicytics is capable of extracting. + +**Any file with `_` is not counted here, do note they may range from custom libraries to special files/wrappers** + +### Want More? + +If there is a specific piece of data that you would like to see extracted by Logicytics, +please let us know. We are constantly working to improve the project and adding new features. + +![Extra Tools](IMG/ExtraTools.png "Here is the inbuilt extra tools menu {BETA}") + +Other than mods, some prefixed tools are in the `EXTRA` directory, use the `--extra` flag to traverse these +special tools + +### Want to create your own mod? + +Check out the [contributing guidlines](CONTRIBUTING.md) file for more info ### Want More? diff --git a/TESTS/TEST_wifi_stealing.py b/TESTS/TEST_wifi_stealing.py new file mode 100644 index 0000000..8d450d2 --- /dev/null +++ b/TESTS/TEST_wifi_stealing.py @@ -0,0 +1,15 @@ +import unittest +from CODE.wifi_stealer import get_password + + +class WiFi(unittest.TestCase): + def test_get_password(self): + print("Running test...") + result = get_password("GEMS-WGP") + print(f"Result: {result}") + self.assertIsNotNone(result) + print("Test completed.") + + +if __name__ == "__main__": + unittest.main() diff --git a/WEB/wiki3.html b/WEB/wiki3.html index a50e817..3f958d6 100644 --- a/WEB/wiki3.html +++ b/WEB/wiki3.html @@ -123,6 +123,8 @@

File Structure

│ └─── /__pycache__ [You may ignore this] ├─── /EXTRA │ └─── # ZIPPED EXTRA FEATURES # +├─── /WEB +│ └─── # Website Files # ├─── /IMG [You may ignore this] └─── /MODS [You add your own mods] diff --git a/requirements.txt b/requirements.txt index 9af2918..68dba91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ requests~=2.32.3 +psutil~=5.9.8 DateTime~=5.5 pathlib~=1.0.1 colorlog~=6.8.2 \ No newline at end of file