diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index cf1f9ca..05540e4 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,3 +1,5 @@
* @DefinetlyNotAI
wifi_stealer.py @ski-sketch
-packet_sniffer.py @ski-sketch
\ No newline at end of file
+packet_sniffer.py @ski-sketch
+bluetooth_details.py @ski-sketch
+bluetooth_logger.py @ski-sketch
\ No newline at end of file
diff --git a/.idea/Logicytics.iml b/.idea/Logicytics.iml
index 235b40b..b6f2739 100644
--- a/.idea/Logicytics.iml
+++ b/.idea/Logicytics.iml
@@ -18,6 +18,7 @@
+
diff --git a/.idea/csv-editor.xml b/.idea/csv-editor.xml
deleted file mode 100644
index cb2fb40..0000000
--- a/.idea/csv-editor.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CODE/Logicytics.py b/CODE/Logicytics.py
index 381becb..e78b9a6 100644
--- a/CODE/Logicytics.py
+++ b/CODE/Logicytics.py
@@ -83,14 +83,14 @@ def get_flags():
If the flags are not a tuple, it prints the help message and exits the program.
"""
+ global ACTION, SUB_ACTION
if isinstance(Flag.data(), tuple):
- global ACTION, SUB_ACTION
try:
# Get flags
ACTION, SUB_ACTION = Flag.data()
- except Exception:
- ACTIONS = Flag.data()
- ACTION = ACTIONS[0]
+ except ValueError:
+ actions = Flag.data()
+ ACTION = actions[0]
SUB_ACTION = None
else:
parser = Flag.data()
@@ -274,12 +274,12 @@ def threaded_execution(execution_list_thread, index_thread):
log.debug("Using threading")
threads = []
- EXECUTION_LIST = generate_execution_list()
- for index, file in enumerate(EXECUTION_LIST):
+ execution_list = generate_execution_list()
+ for index, _ in enumerate(execution_list):
thread = threading.Thread(
target=threaded_execution,
args=(
- EXECUTION_LIST,
+ execution_list,
index,
),
)
@@ -290,14 +290,14 @@ def threaded_execution(execution_list_thread, index_thread):
thread.join()
elif ACTION == "performance_check":
execution_times = []
- EXECUTION_LIST = generate_execution_list()
- for file in range(len(EXECUTION_LIST)):
+ execution_list = generate_execution_list()
+ for file in range(len(execution_list)):
start_time = datetime.now()
- log.parse_execution(Execute.script(EXECUTION_LIST[file]))
+ log.parse_execution(Execute.script(execution_list[file]))
end_time = datetime.now()
elapsed_time = end_time - start_time
execution_times.append((file, elapsed_time))
- log.info(f"{EXECUTION_LIST[file]} executed in {elapsed_time}")
+ log.info(f"{execution_list[file]} executed in {elapsed_time}")
table = PrettyTable()
table.field_names = ["Script", "Execution Time"]
@@ -313,10 +313,10 @@ def threaded_execution(execution_list_thread, index_thread):
log.info("Performance check complete! Performance log found in ACCESS/LOGS/PERFORMANCE")
else:
try:
- EXECUTION_LIST = generate_execution_list()
- for file in range(len(EXECUTION_LIST)): # Loop through List
- log.parse_execution(Execute.script(EXECUTION_LIST[file]))
- log.info(f"{EXECUTION_LIST[file]} executed")
+ execution_list = generate_execution_list()
+ for script in execution_list: # Loop through List
+ log.parse_execution(Execute.script(script))
+ log.info(f"{script} executed")
except UnicodeDecodeError as e:
log.error(f"Error in code: {e}")
except Exception as e:
diff --git a/CODE/_dev.py b/CODE/_dev.py
index 18755ea..e08574d 100644
--- a/CODE/_dev.py
+++ b/CODE/_dev.py
@@ -48,7 +48,7 @@ def _prompt_user(question: str, file_to_open: str = None, special: bool = False)
"""
try:
answer = input(question + " (yes or no):- ")
- if answer.lower() != "yes":
+ if answer.lower() != "yes" or answer.lower() != "y":
if file_to_open:
subprocess.run(["start", file_to_open], shell=True)
if not special:
@@ -88,9 +88,20 @@ def dev_checks() -> None:
# Get the list of code files in the current directory
files = Get.list_of_code_files(".")
- added_files = [f.replace('"', '') for f in files if f not in CURRENT_FILES]
- removed_files = [f.replace('"', '') for f in CURRENT_FILES if f not in files]
- normal_files = [f.replace('"', '') for f in files if f in CURRENT_FILES]
+ added_files, removed_files, normal_files = [], [], []
+ clean_files_list = [file.replace('"', '') for file in CURRENT_FILES]
+
+ for f in files:
+ clean_f = f.replace('"', '')
+ if clean_f in clean_files_list:
+ normal_files.append(clean_f)
+ else:
+ added_files.append(clean_f)
+
+ for f in clean_files_list:
+ clean_f = f.replace('"', '')
+ if clean_f not in files:
+ removed_files.append(clean_f)
# Print the list of added, removed, and normal files in color
print("\n".join([f"\033[92m+ {file}\033[0m" for file in added_files])) # Green +
diff --git a/CODE/bluetooth_details.py b/CODE/bluetooth_details.py
new file mode 100644
index 0000000..d257485
--- /dev/null
+++ b/CODE/bluetooth_details.py
@@ -0,0 +1,119 @@
+import subprocess
+import json
+from logicytics import Log, DEBUG
+
+if __name__ == "__main__":
+ log = Log({"log_level": DEBUG})
+
+
+def get_bluetooth_device_details():
+ """
+ Retrieves and logs detailed information about Bluetooth devices on the system.
+
+ The function runs a PowerShell command to query devices whose names contain the term 'Bluetooth'.
+ It writes the information to a text file named 'Bluetooth Info.txt'.
+
+ Information for each device includes:
+ - Name
+ - Device ID
+ - Description
+ - Manufacturer
+ - Status
+ - PNP Device ID
+
+ Logs errors if any issues are encountered during the process.
+
+ Returns:
+ None
+ """
+ log.info("Fetching detailed info for Bluetooth devices...")
+ try:
+ devices = _query_bluetooth_devices()
+ _write_device_info_to_file(devices, "Bluetooth Info.txt")
+ except Exception as e:
+ log.error(f"Error: {e}")
+ exit(1)
+
+
+def _query_bluetooth_devices():
+ """
+ Queries the system for Bluetooth devices using PowerShell commands.
+
+ Returns:
+ list: A list of device information dictionaries.
+ """
+ try:
+ # Run PowerShell command to get Bluetooth devices
+ command = (
+ "Get-PnpDevice | Where-Object { $_.FriendlyName -like '*Bluetooth*' } | "
+ "Select-Object FriendlyName, DeviceID, Description, Manufacturer, Status, PnpDeviceID | "
+ "ConvertTo-Json -Depth 3"
+ )
+ result = subprocess.run(["powershell", "-Command", command], capture_output=True, text=True, check=True)
+ devices = json.loads(result.stdout)
+ except subprocess.CalledProcessError as e:
+ log.error(f"Failed to query Bluetooth devices: {e}")
+ exit(1)
+ except json.JSONDecodeError as e:
+ log.error(f"Failed to parse device information: {e}")
+ exit(1)
+
+ if isinstance(devices, dict):
+ devices = [devices] # Handle single result case
+
+ device_info_list = []
+ for device in devices:
+ device_info = {
+ 'Name': device.get('FriendlyName', 'Unknown'),
+ 'Device ID': device.get('DeviceID', 'Unknown'),
+ 'Description': device.get('Description', 'Unknown'),
+ 'Manufacturer': device.get('Manufacturer', 'Unknown'),
+ 'Status': device.get('Status', 'Unknown'),
+ 'PNP Device ID': device.get('PnpDeviceID', 'Unknown')
+ }
+ log.debug(f"Retrieved device: {device_info['Name']}")
+ device_info_list.append(device_info)
+
+ return device_info_list
+
+
+def _write_device_info_to_file(devices, filename):
+ """
+ Writes the details of the Bluetooth devices to a file.
+
+ Args:
+ devices (list): List of device information dictionaries.
+ filename (str): Name of the file to write to.
+
+ Returns:
+ None
+ """
+ try:
+ with open(filename, "w", encoding="UTF-8") as file:
+ for device_info in devices:
+ _write_single_device_info(file, device_info)
+ except Exception as e:
+ log.error(f"Failed to write device information to file: {e}")
+ exit(1)
+
+
+def _write_single_device_info(file, device_info):
+ """
+ Writes information for a single Bluetooth device to the file.
+
+ Args:
+ file (TextIO): File object to write to.
+ device_info (dict): Dictionary containing device information.
+
+ Returns:
+ None
+ """
+ file.write(f"Name: {device_info.get('Name', 'Unknown')}\n")
+ for key, value in device_info.items():
+ if key != 'Name':
+ file.write(f" {key}: {value}\n")
+ file.write("\n") # Separate devices with a blank line
+
+
+if __name__ == "__main__":
+ get_bluetooth_device_details()
diff --git a/CODE/bluetooth_logger.py b/CODE/bluetooth_logger.py
new file mode 100644
index 0000000..39afa0e
--- /dev/null
+++ b/CODE/bluetooth_logger.py
@@ -0,0 +1,109 @@
+import subprocess
+import re
+import datetime
+from logicytics import Log, DEBUG
+
+if __name__ == "__main__":
+ log = Log({"log_level": DEBUG})
+
+
+# Utility function to log data to a file
+def save_to_file(filename, section_title, data):
+ """Logs data to a text file with a section title."""
+ try:
+ with open(filename, 'a', encoding='utf-8') as file:
+ file.write(f"\n{'=' * 50}\n{section_title}\n{'=' * 50}\n")
+ file.write(f"{data}\n" if isinstance(data, str) else "\n".join(data) + "\n")
+ file.write(f"{'=' * 50}\n")
+ except Exception as e:
+ log.error(f"Error writing to file {filename}: {e}")
+
+
+# Utility function to run PowerShell commands
+def run_powershell_command(command):
+ """Runs a PowerShell command and returns the output."""
+ try:
+ result = subprocess.run(["powershell", "-Command", command], capture_output=True, text=True)
+ if result.returncode != 0:
+ log.error(f"PowerShell command failed with return code {result.returncode}")
+ return []
+ return result.stdout.splitlines()
+ except Exception as e:
+ log.error(f"Error running PowerShell command: {e}")
+ return []
+
+
+# Unified parsing function for PowerShell output
+def parse_output(lines, regex, group_names):
+ """Parses command output using a regex and extracts specified groups."""
+ results = []
+ for line in lines:
+ match = re.match(regex, line)
+ if match:
+ results.append({name: match.group(name) for name in group_names})
+ else:
+ log.debug(f"Skipping unrecognized line: {line}")
+ return results
+
+
+# Function to get paired Bluetooth devices
+def get_paired_bluetooth_devices():
+ """Retrieves paired Bluetooth devices with names and MAC addresses."""
+ command = (
+ 'Get-PnpDevice -Class Bluetooth | Where-Object { $_.Status -eq "OK" } | Select-Object Name, DeviceID'
+ )
+ output = run_powershell_command(command)
+ log.debug(f"Raw PowerShell output for paired devices:\n{output}")
+
+ devices = parse_output(
+ output,
+ regex=r"^(?P.+?)\s+(?P.+)$",
+ group_names=["Name", "DeviceID"]
+ )
+
+ # Extract MAC addresses
+ for device in devices:
+ mac_match = re.search(r"BLUETOOTHDEVICE_(?P[A-F0-9]{12})", device["DeviceID"], re.IGNORECASE)
+ device["MAC"] = mac_match.group("MAC") if mac_match else "Address Not Found"
+
+ return [f"Name: {device['Name']}, MAC: {device['MAC']}" for device in devices]
+
+
+# Function to log all Bluetooth data
+def log_bluetooth():
+ """Logs Bluetooth device data and event logs."""
+ log.info("Starting Bluetooth data logging...")
+ filename = "bluetooth_data.txt"
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ save_to_file(filename, "Bluetooth Data Collection - Timestamp", timestamp)
+
+ # Collect and log paired devices
+ log.info(f"Collecting paired devices...")
+ paired_devices = get_paired_bluetooth_devices()
+ section_title = "Paired Bluetooth Devices"
+ save_to_file(filename, section_title, paired_devices or ["No paired Bluetooth devices found."])
+ log.debug(f"{section_title}: {paired_devices}")
+
+ # Collect and log event logs
+ def collect_logs(title, command):
+ logs = run_powershell_command(command)
+ save_to_file(filename, title, logs or ["No logs found."])
+ log.info(f"Getting {title}...")
+
+ collect_logs(
+ "Bluetooth Connection/Disconnection Logs",
+ 'Get-WinEvent -LogName "Microsoft-Windows-Bluetooth-BthLEServices/Operational" '
+ '| Select-Object TimeCreated, Id, Message | Format-Table -AutoSize'
+ )
+
+ collect_logs(
+ "Bluetooth File Transfer Logs",
+ 'Get-WinEvent -LogName "Microsoft-Windows-Bluetooth-BthLEServices/Operational" '
+ '| Select-String -Pattern "file.*transferred" | Format-Table -AutoSize'
+ )
+
+ log.info("Finished Bluetooth data logging.")
+
+
+if __name__ == "__main__":
+ log_bluetooth()
diff --git a/CODE/config.ini b/CODE/config.ini
index 65a2e6d..108a1a3 100644
--- a/CODE/config.ini
+++ b/CODE/config.ini
@@ -9,8 +9,8 @@ delete_old_logs = false
[System Settings]
# Do not play with these settings unless you know what you are doing
-version = 3.1.0
-files = "browser_miner.ps1, cmd_commands.py, dir_list.py, dump_memory.py, event_log.py, Logicytics.py, log_miner.py, media_backup.py, netadapter.ps1, packet_sniffer.py, property_scraper.ps1, registry.py, sensitive_data_miner.py, ssh_miner.py, sys_internal.py, tasklist.py, tree.ps1, vulnscan.py, wifi_stealer.py, window_feature_miner.ps1, wmic.py, _debug.py, _dev.py, _extra.py, logicytics\Checks.py, logicytics\Execute.py, logicytics\FileManagement.py, logicytics\Flag.py, logicytics\Get.py, logicytics\Logger.py, logicytics\__init__.py, VulnScan\tools\_study_network.py, VulnScan\tools\_test_gpu_acceleration.py, VulnScan\tools\_vectorizer.py, VulnScan\v2-deprecated\_generate_data.py, VulnScan\v2-deprecated\_train.py, VulnScan\v3\_generate_data.py, VulnScan\v3\_train.py"
+version = 3.1.1
+files = "bluetooth_details.py, bluetooth_logger.py, browser_miner.ps1, cmd_commands.py, dir_list.py, dump_memory.py, event_log.py, Logicytics.py, log_miner.py, media_backup.py, netadapter.ps1, packet_sniffer.py, property_scraper.ps1, registry.py, sensitive_data_miner.py, ssh_miner.py, sys_internal.py, tasklist.py, tree.ps1, vulnscan.py, wifi_stealer.py, window_feature_miner.ps1, wmic.py, _debug.py, _dev.py, _extra.py, logicytics\Checks.py, logicytics\Execute.py, logicytics\FileManagement.py, logicytics\Flag.py, logicytics\Get.py, logicytics\Logger.py, logicytics\__init__.py, VulnScan\tools\_study_network.py, VulnScan\tools\_test_gpu_acceleration.py, VulnScan\tools\_vectorizer.py, VulnScan\v2-deprecated\_generate_data.py, VulnScan\v2-deprecated\_train.py, VulnScan\v3\_generate_data.py, VulnScan\v3\_train.py"
###################################################
# The following settings are for specific modules #
diff --git a/CODE/packet_sniffer.py b/CODE/packet_sniffer.py
index 5b02bf9..d016d02 100644
--- a/CODE/packet_sniffer.py
+++ b/CODE/packet_sniffer.py
@@ -78,7 +78,7 @@ def get_port_info(packet: IP, port_type: str) -> int | None:
@log.function
def print_packet_summary(packet_info: dict):
"""Prints a summary of the captured packet."""
- log.info(f"Packet captured: {packet_info['protocol']} packet from {packet_info['src_ip']} "
+ log.debug(f"Packet captured: {packet_info['protocol']} packet from {packet_info['src_ip']} "
f"to {packet_info['dst_ip']} | Src Port: {packet_info['src_port']} | Dst Port: {packet_info['dst_port']}")
@@ -162,7 +162,7 @@ def visualize_graph(node_colors: str = None, node_sizes: str = None):
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.title("Network Connections Graph")
plt.savefig("network_connections_graph.png")
- plt.show()
+ plt.close()
@log.function
@@ -171,8 +171,13 @@ def main():
packet_count = int(config['packet_count'])
timeout = int(config['timeout'])
- if packet_count == 0 or timeout == 0:
- log.error("Invalid packet count or timeout value. Please check the configuration.")
+ if packet_count <= 0 or timeout <= 0:
+ log.error(
+ f"Oops! Can't work with these values:\n"
+ f"- Packet count: {packet_count} {'❌ (must be > 0)' if packet_count <= 0 else '✅'}\n"
+ f"- Timeout: {timeout} {'❌ (must be > 0)' if timeout <= 0 else '✅'}"
+ )
+ exit(1)
try:
start_sniffing(interface, packet_count, timeout)
diff --git a/README.md b/README.md
index 7a3e5a8..fc304eb 100644
--- a/README.md
+++ b/README.md
@@ -213,6 +213,8 @@ Here are some of the data points that Logicytics extracts:
| event_logs.py | Produces a multiple txt files in a folder on many event logs (Security, Applications and System) | |
| vulnscan.py | Uses AI/ML to detect sensitive files, and log their paths | In beta! |
| dump_memory.py | Dumps some memory as well as log some RAM details | |
+| bluetooth_details.py | Gets the PNP Device ID, Status, Manufacturer, Device ID, Name, Description of all paired bluetooth devices | |
+| bluetooth_logger.py | Collect, log, and analyze Bluetooth-related data, by accessing the Windows registry and Event Viewer. | |
This is not an exhaustive list,
but it should give you a good idea of what data Logicytics is capable of extracting.
diff --git a/requirements.txt b/requirements.txt
index 31f4663..f463140 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,9 +2,14 @@ configobj~=5.0.9
joblib~=1.3.2
matplotlib~=3.8.4
xgboost~=2.1.3
-scikit-learn~=1.6.0
+scikit-learn~=1.5.2
Faker~=30.3.0
+networkx~=3.2.1
numpy~=1.26.4
+plotly~=5.24.1
+seaborn~=0.13.2
+torchviz~=0.0.3
+tqdm~=4.66.6
transformers~=4.38.2
requests~=2.32.3
psutil~=6.1.0
@@ -14,9 +19,4 @@ colorlog~=6.9.0
safetensors~=0.4.5
prettytable~=3.12.0
pandas~=2.2.2
-networkx~=3.2.1
-scapy~=2.5.0
-seaborn~=0.13.2
-torchviz~=0.0.3
-plotly~=5.24.1
-tqdm~=4.66.6
+scapy~=2.5.0
\ No newline at end of file