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