Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ $RECYCLE.BIN/
*.pyc
/CODE/SysInternal_Suite/.sys.ignore
/ACCESS/
/CODE/VulnScan/tools/NN features/
/CODE/vulnscan/tools/NN features/
/CODE/logicytics/User_History.json.gz
/CODE/logicytics/User_History.json
/CODE/SysInternal_Suite/psfile.exe
Expand Down
18 changes: 9 additions & 9 deletions .idea/Logicytics.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/webResources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

107 changes: 20 additions & 87 deletions CODE/Logicytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@

import gc
import os
import shutil
import subprocess
import sys
import zipfile
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime

import psutil
from prettytable import PrettyTable

from logicytics import Log, execute, check, get, file_management, flag, DEBUG, DELETE_LOGS, config
Expand Down Expand Up @@ -200,90 +197,34 @@ def __performance(self):
log.warning("Advised to turn on DEBUG logging!!")

execution_times = []
memory_usage = []
process = psutil.Process(os.getpid())

for file in range(len(self.execution_list)):
gc.collect()
start_time = datetime.now()
start_memory = process.memory_full_info().uss / 1024 / 1024 # MB
log.execution(execute.script(self.execution_list[file]))
end_time = datetime.now()
end_memory = process.memory_full_info().uss / 1024 / 1024 # MB
elapsed_time = end_time - start_time
memory_delta = max(0, end_memory - start_memory) # Clamps negative delta to 0
memory_usage.append((self.execution_list[file], f"{memory_delta}"))
execution_times.append((self.execution_list[file], elapsed_time))
log.info(f"{self.execution_list[file]} executed in {elapsed_time}")
try:
if (end_memory - start_memory) < 0:
log.info(
f"{self.execution_list[file]} used {memory_delta:.3f}MB of memory - \033[33mPossible Affected by outside processes\033[0m")
else:
log.info(f"{self.execution_list[file]} used {memory_delta:.3f}MB of memory")
except Exception as e:
log.warning("Failed to log memory usage delta, reason: " + str(e))
log.debug(f"Started with {start_memory:.3f}MB of memory and ended with {end_memory:.3f}MB of memory")

table = PrettyTable()
table.field_names = ["Script", "Execution Time", "Memory Usage (MB)"]
table.field_names = ["Script", "Execution Time"]
for script, elapsed_time in execution_times:
try:
memory = f"{float(next(m[1] for m in memory_usage if m[0] == script)):.3f}"
except StopIteration:
log.warning(f"No memory data found for {script}")
memory = "N/A"
except Exception as e:
log.warning(f"Failed to log memory usage for {script}, reason: " + str(e))
memory = "N/A"
table.add_row([script, elapsed_time, f"{memory}"])
table.add_row([script, elapsed_time])

try:
with open(
f"../ACCESS/LOGS/PERFORMANCE/Performance_Summary_"
f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt", "w"
) as f:
f.write(table.get_string())
f.write(
"\nSome values may be negative, Reason may be due to external resources playing with memory usage,\n"
"Close background tasks to get more accurate readings\n\n")
f.write("Note: This is not a low-level memory logger, data here isn't 100% accurate!\n")
f.write("\nNote: This test only measures execution time.\n")
log.info("Performance check complete! Performance log found in ACCESS/LOGS/PERFORMANCE")
except Exception as e:
log.error(f"Error writing performance log: {e}")


class SpecialAction:
@staticmethod
def backup(directory: str, name: str):
"""
Creates a backup of a specified directory by zipping its contents and moving it to a designated backup location.

Args:
directory (str): The path to the directory to be backed up.
name (str): The name of the backup file.

Returns:
None
"""
if not os.path.exists(directory):
log.critical(f"Directory {directory} does not exist!")
return

# Check if backup exists, delete it if so
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:
for root, dirs, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
relative_path = os.path.relpath(str(file_path), start=os.getcwd())
zip_file.write(str(file_path), arcname=relative_path)

shutil.move(f"{name}.zip", "../ACCESS/BACKUP")

@staticmethod
def update() -> tuple[str, str]:
"""
Expand All @@ -296,12 +237,18 @@ def update() -> tuple[str, str]:
str: The output from the git pull command.
"""
# Check if git command is available
if subprocess.run(["git", "--version"], capture_output=True).returncode != 0:
try:
if subprocess.run(["git", "--version"], capture_output=True).returncode != 0:
return "Git is not installed or not available in the PATH.", "error"
except FileNotFoundError:
return "Git is not installed or not available in the PATH.", "error"

# Check if the project is a git repository
if not os.path.exists(os.path.join(os.getcwd(), "../.git")):
return "This project is not a git repository. The update flag uses git.", "error"
try:
if not os.path.exists(os.path.join(os.getcwd(), "../.git")):
return "This project is not a git repository. The update flag uses git.", "error"
except Exception as e:
return f"Error checking for git repository: {e}", "error"

current_dir = os.getcwd()
parent_dir = os.path.dirname(current_dir)
Expand Down Expand Up @@ -402,25 +349,11 @@ def handle_special_actions():
input("Press Enter to exit...")
exit(0)

if ACTION == "restore":
log.warning(
"Sorry, this feature is yet to be implemented. You can manually Restore your backups, We will open "
"the location for you"
)
file_management.open_file("../ACCESS/BACKUP/")
if ACTION == "usage":
flag.Match.generate_summary_and_graph()
input("Press Enter to exit...")
exit(1)

if ACTION == "backup":
log.info("Backing up...")
SpecialAction.backup(".", "Default_Backup")
log.debug("Backup complete -> CODE dir")
SpecialAction.backup("../MODS", "Mods_Backup")
log.debug("Backup complete -> MODS dir")
log.info("Backup complete!")
input("Press Enter to exit...")
exit(0)


def check_privileges():
"""
Expand Down Expand Up @@ -484,12 +417,12 @@ def handle_sub_action():
"""
log.info("Completed successfully!")
log.newline()
if ACTION == "performance_check":
return # Do not handle sub actions for performance check
if SUB_ACTION == "shutdown":
subprocess.call("shutdown /s /t 3", shell=False)
elif SUB_ACTION == "reboot":
subprocess.call("shutdown /r /t 3", shell=False)
# Do not handle sub actions for performance check
if ACTION != "performance_check":
if SUB_ACTION == "shutdown":
subprocess.call("shutdown /s /t 3", shell=False)
elif SUB_ACTION == "reboot":
subprocess.call("shutdown /r /t 3", shell=False)


@log.function
Expand Down
28 changes: 16 additions & 12 deletions CODE/_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import psutil
import requests

from logicytics import Log, DEBUG, VERSION, check, config
from logicytics import Log, DEBUG, VERSION, check, config, get

log_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "ACCESS\\LOGS\\DEBUG\\DEBUG.log")
log = Log({"log_level": DEBUG, "filename": log_path, "truncate_message": False, "delete_log": True})
Expand Down Expand Up @@ -47,14 +47,16 @@ def check_required_files(directory: str, required_files: list[str]):
log.error(f"Directory {directory} does not exist.")
return

actual_files = []
for root, _, files in os.walk(directory):
for file in files:
relative_path = os.path.relpath(os.path.join(root, file), start=directory)
actual_files.append(relative_path.replace("\\", "/").replace('"', '')) # Normalize paths
# Use get.list_of_files to retrieve files, excluding specified files, dirs, and extensions
actual_files = get.list_of_files(
directory,
exclude_files=["logicytics/User_History.json.gz", "logicytics/User_History.json"],
exclude_dirs=["SysInternal_Suite"],
exclude_extensions=[".pyc"]
)
actual_files = [f.replace("\\", "/").replace('"', '') for f in actual_files] # Normalize paths

log.debug(f"Actual files found: {actual_files}")

# Strip quotes and normalize paths for comparison
normalized_required_files = [
required_file.strip().replace("\\", "/").replace('"', '') # Remove quotes and normalize paths
Expand Down Expand Up @@ -130,19 +132,21 @@ def python_version():
"""
version = sys.version.split()[0]
MIN_VERSION = (3, 11)
MAX_VERSION = (3, 13)
MAX_VERSION = (3, 14)
try:
major, minor = map(int, version.split(".")[:2])
if MIN_VERSION <= (major, minor) < MAX_VERSION:
log.info(f"Python Version: {version} - Perfect")
if (major, minor) == MIN_VERSION:
log.info(f"Python Version: {version} - Perfect (mainly tested on 3.11.x)")
else:
log.info(f"Python Version: {version} - Supported")
elif (major, minor) < MIN_VERSION:
log.warning(f"Python Version: {version} - Recommended: 3.11.x")
else:
log.error(f"Python Version: {version} - Incompatible")
except Exception as e:
log.error(f"Failed to parse Python Version: {e}")


class ConfigManager:
@staticmethod
def get_online_config() -> dict | None:
Expand Down Expand Up @@ -206,8 +210,8 @@ def debug():
SysInternalManager.check_binaries("SysInternal_Suite")

# System Checks
log.info("Admin privileges found" if check.admin() else "Admin privileges not found")
log.info("UAC enabled" if check.uac() else "UAC disabled")
log.info("Admin privileges found") if check.admin() else log.warning("Admin privileges not found")
log.info("UAC enabled") if check.uac() else log.warning("UAC disabled")
log.info(f"Execution path: {psutil.__file__}")
log.info(f"Global execution path: {sys.executable}")
log.info(f"Local execution path: {sys.prefix}")
Expand Down
10 changes: 5 additions & 5 deletions CODE/_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import configobj

from logicytics import log, get, file_management, CURRENT_FILES, VERSION
from logicytics import log, get, CURRENT_FILES, VERSION


def color_print(text, color="reset", is_input=False) -> None | str:
Expand Down Expand Up @@ -150,12 +150,13 @@ def _handle_file_operations() -> None:
while True:
version = color_print(f"[?] Enter the new version of the project (Old version is {VERSION}): ", "cyan",
is_input=True)
if attempts >= max_attempts:
color_print("[x] Maximum attempts reached. Please run the script again.", "red")
exit()

if re.match(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$", version):
_update_ini_file("config.ini", version, "version")
break
elif attempts >= max_attempts:
color_print("[x] Maximum attempts reached. Please run the script again.", "red")
exit()
else:
color_print("[!] Please enter a valid version number (e.g., 1.2.3)", "yellow")
attempts += 1
Expand Down Expand Up @@ -189,7 +190,6 @@ def dev_checks() -> None:
- Updates configuration file with current files and version
- Logs warnings or errors during the process
"""
file_management.mkdir()
if not _perform_checks():
return
_handle_file_operations()
Expand Down
6 changes: 2 additions & 4 deletions CODE/logicytics/FileManagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ def open_file(file: str, use_full_path: bool = False) -> str | None:
@staticmethod
def mkdir():
"""
Creates the necessary directories for storing logs, backups, and data.
Creates the necessary directories for storing logs, and data.

This method ensures the existence of specific directory structures used by the application, including:
- Log directories for general, debug, and performance logs
- Backup directory
- Data directories for storing hashes and zip files

The method uses `os.makedirs()` with `exist_ok=True` to create directories without raising an error if they already exist.
Expand All @@ -50,7 +49,6 @@ def mkdir():
os.makedirs("../ACCESS/LOGS/", exist_ok=True)
os.makedirs("../ACCESS/LOGS/DEBUG", exist_ok=True)
os.makedirs("../ACCESS/LOGS/PERFORMANCE", 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)

Expand Down Expand Up @@ -94,7 +92,7 @@ def __get_files_to_zip(path: str) -> list:
"""
excluded_extensions = (".py", ".exe", ".bat", ".ps1", ".pkl", ".pth")
excluded_prefixes = ("config.ini", "SysInternal_Suite",
"__pycache__", "logicytics", "VulnScan")
"__pycache__", "logicytics", "vulnscan")

return [
f for f in os.listdir(path)
Expand Down
Loading