Skip to content

Commit 0ea294b

Browse files
v3.6.0 Update features (#235)
2 parents cb27001 + bf9cf3f commit 0ea294b

14 files changed

+414
-350
lines changed

.idea/csv-editor.xml

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CODE/Logicytics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ def __and_log(directory: str, name: str):
441441
log.debug(
442442
f"Zipping directory '{directory}' with name '{name}' under action '{ACTION}'"
443443
)
444+
# noinspection PyUnreachableCode
444445
zip_values = file_management.Zip.and_hash(
445446
directory,
446447
name,

CODE/config.ini

Lines changed: 10 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ save_preferences = true
2626
[System Settings]
2727
# Do not play with these settings unless you know what you are doing
2828
# Dev Mode allows a safe way to modify these settings!!
29-
version = 3.5.1
30-
files = "bluetooth_details.py, bluetooth_logger.py, browser_miner.ps1, cmd_commands.py, config.ini, dir_list.py, dump_memory.py, event_log.py, Logicytics.py, log_miner.py, media_backup.py, netadapter.ps1, network_psutil.py, 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, logicytics\Checks.py, logicytics\Config.py, logicytics\Execute.py, logicytics\FileManagement.py, logicytics\Flag.py, logicytics\Get.py, logicytics\Logger.py, logicytics\User_History.json.gz, vulnscan\SenseMini.3n3.pth, vulnscan\vectorizer.3n3.pkl"
29+
version = 3.6.0
30+
files = "bluetooth_details.py, bluetooth_logger.py, browser_miner.ps1, cmd_commands.py, config.ini, dir_list.py, dump_memory.py, encrypted_drive_audit.py, event_log.py, Logicytics.py, log_miner.py, media_backup.py, netadapter.ps1, network_psutil.py, packet_sniffer.py, property_scraper.ps1, registry.py, sensitive_data_miner.py, ssh_miner.py, sys_internal.py, tasklist.py, tree.ps1, usb_history.py, vulnscan.py, wifi_stealer.py, window_feature_miner.ps1, wmic.py, logicytics\Checks.py, logicytics\Config.py, logicytics\Execute.py, logicytics\FileManagement.py, logicytics\Flag.py, logicytics\Get.py, logicytics\Logger.py, logicytics\User_History.json.gz, vulnscan\Model_SenseMacro.4n1.pth"
3131
# If you forked the project, change the USERNAME to your own to use your own fork as update material,
3232
# I dont advise doing this however
3333
config_url = https://raw.githubusercontent.com/DefinetlyNotAI/Logicytics/main/CODE/config.ini
@@ -100,93 +100,15 @@ timeout = 10
100100
max_retry_time = 30
101101

102102
###################################################
103+
103104
[VulnScan Settings]
104-
# Following extensions to be skipped by the model
105-
# Format: comma-separated list with dots (e.g., .exe, .dll)
106-
unreadable_extensions = .exe, .dll, .so, .zip, .tar, .gz, .7z, .rar, .jpg, .jpeg, .png, .gif, .bmp, .tiff, .webp, .mp3, .wav, .flac, .aac, .ogg, .mp4, .mkv, .avi, .mov, .wmv, .flv, .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .odt, .ods, .odp, .bin, .dat, .iso, .class, .pyc, .o, .obj, .sqlite, .db, .ttf, .otf, .woff, .woff2, .lnk, .url
107-
# In MB, max file size that the model is allowed to scan, if commented out disables the limit, you can also just say None
108-
max_file_size_mb = None
109-
# Max workers to be used, either integer or use auto to make it decide the best value
105+
# Max characters of text from each file to analyze. Set an integer or None to disable truncation.
106+
text_char_limit = None
107+
# Max workers to be used, either integer or use "auto" to make it decide the best value
110108
max_workers = auto
111-
112-
[VulnScan.generate Settings]
113-
# The following settings are for the Generate module for fake training data
114-
extensions = .txt, .log, .md, .csv, .json, .xml, .html, .yaml, .ini, .pdf, .docx, .xlsx, .pptx
115-
save_path = PATH
116-
117-
# Options include:
118-
# 'Sense' - Generates 50k files, each 25KB in size.
119-
# 'SenseNano' - Generates 5 files, each 5KB in size.
120-
# 'SenseMacro' - Generates 1m files, each 10KB in size.
121-
# 'SenseMini' - Generates 10k files, each 10KB in size.
122-
# 'SenseCustom' - Uses custom size settings from the configuration file.
123-
code_name = SenseMini
124-
125-
# This allows more randomness in the file sizes, use 0 to disable
126-
# this is applied randomly every time a file is generated
127-
# Variation is applied in the following way:
128-
# size +- (size */ variation) where its random weather to add or subtract and divide or multiply
129-
size_variation = 0.1
130-
131-
# Set to SenseCustom to use below size settings
132-
min_file_size = 5KB
133-
max_file_size = 50KB
134-
135-
# Chances for the following data types in files:
136-
# 0.0 - 1.0, the rest will be for pure data
137-
full_sensitive_chance = 0.07
138-
partial_sensitive_chance = 0.2
139-
140-
[VulnScan.vectorizer Settings]
141-
# The following settings are for the Vectorizer module for vectorizing data
142-
# Usually it automatically vectorizes data, but this is for manual vectorization
143-
144-
# We advise to use this vectorization, although not knowing the vectorizer is not advised
145-
# as this may lead to ValueErrors due to different inputs
146-
# Use the vectorizer supplied for any v3 model on SenseMini
147-
148-
# The path to the data to vectorize, either a file or a directory
149-
data_path = PATH
150-
# The path to save the vectorized data - It will automatically be appended '\Vectorizer.pkl'
151-
# Make sure the path is a directory, and it exists
152-
output_path = PATH
153-
154-
# Vectorizer to use, options include:
155-
# tfidf or count - The code for the training only supports tfidf - we advise to use tfidf
156-
vectorizer_type = tfidf
157-
158-
[VulnScan.train Settings]
159-
# The following settings are for the Train module for training models
160-
# NeuralNetwork seems to be the best choice for this task
161-
# Options: "NeuralNetwork", "LogReg",
162-
# "RandomForest", "ExtraTrees", "GBM",
163-
# "XGBoost", "DecisionTree", "NaiveBayes"
164-
model_name = NeuralNetwork
165-
166-
# General Training Parameters
167-
epochs = 10
168-
batch_size = 32
169-
learning_rate = 0.001
170-
use_cuda = true
171-
172-
# Paths to train and save data
173-
train_data_path = PATH
174-
# If all models are to be trained, this is the path to save all models,
175-
# and will be appended with the model codename and follow naming convention
176-
save_model_path = PATH
177-
178-
[VulnScan.study Settings]
179-
# Here is the basics of the study module
180-
# This is useful to generate graphs and data that may help in understanding the model
181-
# Everything is found online pre-studied, so this is not necessary
182-
# But it is useful for understanding the model locally
183-
# All files be saved here, and can't be changed, PATH is "NN features/"
184-
185-
# This is the path to the model, and the vectorizer
186-
model_path = PATH
187-
vectorizer_path = PATH
188-
# Number of features to visualise in the SVG Bar graph, maximum is 3000 due to limitations
189-
# Placing -1 will visualise first 3000 features. Bar will be a color gradient heatmap.
190-
number_of_features = -1
109+
# Sensitivity threshold (0.0–1.0) for the model to flag content as sensitive
110+
threshold = 0.6
111+
# Paths for required files
112+
model = vulnscan/Model_SenseMacro.4n1.pth
191113

192114
##################################################

CODE/encrypted_drive_audit.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import datetime
2+
import getpass
3+
import os
4+
import platform
5+
import shutil
6+
import subprocess
7+
from pathlib import Path
8+
9+
from logicytics import check, log
10+
11+
12+
def now_iso():
13+
return datetime.datetime.now().astimezone().isoformat()
14+
15+
16+
def run_cmd(cmd):
17+
log.debug(f"Running command: {cmd}")
18+
try:
19+
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
20+
if proc.returncode == 0:
21+
log.debug(f"Command succeeded: {cmd}")
22+
else:
23+
log.warning(f"Command returned {proc.returncode}: {cmd}")
24+
return proc.stdout.strip(), proc.stderr.strip(), proc.returncode
25+
except FileNotFoundError:
26+
log.error(f"Command not found: {cmd[0]}")
27+
return "", "not found", 127
28+
except subprocess.TimeoutExpired:
29+
log.error(f"Command timed out: {cmd}")
30+
return "", "timeout", 124
31+
32+
33+
def have(cmd_name):
34+
exists = shutil.which(cmd_name) is not None
35+
log.debug(f"Check if '{cmd_name}' exists: {exists}")
36+
return exists
37+
38+
39+
def get_mountvol_output():
40+
log.info("Gathering mounted volumes via mountvol")
41+
out, err, _ = run_cmd(["mountvol"])
42+
if not out:
43+
return err
44+
lines = out.splitlines()
45+
filtered = []
46+
keep = False
47+
for line in lines:
48+
if line.strip().startswith("\\\\?\\Volume"):
49+
keep = True
50+
if keep:
51+
filtered.append(line)
52+
return "\n".join(filtered)
53+
54+
55+
def main():
56+
script_dir = Path(__file__).resolve().parent
57+
report_path = script_dir / "win_encrypted_volume_report.txt"
58+
log.info(f"Starting encrypted volume analysis, report will be saved to {report_path}")
59+
60+
with report_path.open("w", encoding="utf-8") as f:
61+
f.write("=" * 80 + "\n")
62+
f.write("Windows Encrypted Volume Report\n")
63+
f.write("=" * 80 + "\n")
64+
f.write(f"Generated at: {now_iso()}\n")
65+
f.write(f"User: {getpass.getuser()}\n")
66+
f.write(f"IsAdmin: {check.admin()}\n")
67+
f.write(f"Hostname: {platform.node()}\n")
68+
f.write(f"Version: {platform.platform()}\n\n")
69+
70+
# Logical drives
71+
log.info("Gathering logical volumes via wmic")
72+
f.write("Logical Volumes (wmic):\n")
73+
out, err, _ = run_cmd(["wmic", "logicaldisk", "get",
74+
"DeviceID,DriveType,FileSystem,FreeSpace,Size,VolumeName"])
75+
f.write(out + "\n" + err + "\n\n")
76+
77+
# Mounted volumes
78+
f.write("Mounted Volumes (mountvol):\n")
79+
f.write(get_mountvol_output() + "\n\n")
80+
81+
# BitLocker status
82+
f.write("=" * 80 + "\nBitLocker Status\n" + "=" * 80 + "\n")
83+
if have("manage-bde"):
84+
log.info("Checking BitLocker status with manage-bde")
85+
for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
86+
path = f"{letter}:"
87+
if os.path.exists(f"{path}\\"):
88+
out, err, _ = run_cmd(["manage-bde", "-status", path])
89+
f.write(f"Drive {path}:\n{out}\n{err}\n\n")
90+
else:
91+
log.warning("manage-bde not found")
92+
93+
if have("powershell"):
94+
log.info("Checking BitLocker status with PowerShell")
95+
f.write("PowerShell Get-BitLockerVolume:\n")
96+
ps_cmd = r"Get-BitLockerVolume | Format-List *"
97+
out, err, _ = run_cmd(["powershell", "-NoProfile", "-Command", ps_cmd])
98+
f.write(out + "\n" + err + "\n\n")
99+
else:
100+
log.warning("PowerShell not available")
101+
102+
log.info(f"Report successfully saved to {report_path}")
103+
104+
105+
if __name__ == "__main__":
106+
main()

CODE/logicytics/Flag.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def __available_arguments(
498498
"and not the best, use only if the device doesnt have python installed.",
499499
)
500500

501-
# TODO v3.6.0 -> Out of beta
501+
# TODO v3.6.1 -> Out of beta
502502
parser.add_argument(
503503
"--vulnscan-ai",
504504
action="store_true",

CODE/logicytics/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,5 @@ def wrapper(*args, **kwargs) -> callable:
122122
"ObjectLoadError",
123123
"log",
124124
"Log",
125+
"config",
125126
]

CODE/usb_history.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import ctypes
2+
import os
3+
import winreg
4+
from datetime import datetime, timedelta
5+
6+
from logicytics import log
7+
8+
9+
class USBHistory:
10+
def __init__(self):
11+
self.history_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "usb_history.txt")
12+
13+
def _save_history(self, message: str):
14+
"""Append a timestamped message to the history file and log it."""
15+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
16+
entry = f"{timestamp} - {message}\n"
17+
try:
18+
with open(self.history_path, "a", encoding="utf-8") as f:
19+
f.write(entry)
20+
log.debug(f"Saved entry: {message}")
21+
except Exception as e:
22+
log.error(f"Failed to write history: {e}")
23+
24+
# noinspection PyUnresolvedReferences
25+
@staticmethod
26+
def _get_last_write_time(root_key, sub_key_path):
27+
"""Return the precise last write time of a registry key, or None on failure."""
28+
handle = ctypes.wintypes.HANDLE()
29+
try:
30+
advapi32 = ctypes.windll.advapi32
31+
if advapi32.RegOpenKeyExW(root_key, sub_key_path, 0, winreg.KEY_READ, ctypes.byref(handle)) != 0:
32+
return None
33+
ft = ctypes.wintypes.FILETIME()
34+
if advapi32.RegQueryInfoKeyW(handle, None, None, None, None, None, None, None, None, None, None,
35+
ctypes.byref(ft)) != 0:
36+
return None
37+
t = ((ft.dwHighDateTime << 32) + ft.dwLowDateTime) // 10
38+
return datetime(1601, 1, 1) + timedelta(microseconds=t)
39+
finally:
40+
if handle:
41+
ctypes.windll.advapi32.RegCloseKey(handle)
42+
43+
@staticmethod
44+
def _enum_subkeys(root, path, warn_func):
45+
"""Yield all subkeys of a registry key, logging warnings on errors."""
46+
try:
47+
with winreg.OpenKey(root, path) as key:
48+
subkey_count, _, _ = winreg.QueryInfoKey(key)
49+
for i in range(subkey_count):
50+
try:
51+
yield winreg.EnumKey(key, i)
52+
except OSError as e:
53+
if getattr(e, "winerror", None) == 259: # ERROR_NO_MORE_ITEMS
54+
break
55+
warn_func(f"Error enumerating {path} index {i}: {e}")
56+
except OSError as e:
57+
warn_func(f"Failed to open registry key {path}: {e}")
58+
59+
@staticmethod
60+
def _get_friendly_name(dev_info_path, device_id):
61+
"""Return the friendly name of a device if available, else the device ID."""
62+
try:
63+
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, dev_info_path) as dev_key:
64+
return winreg.QueryValueEx(dev_key, "FriendlyName")[0]
65+
except FileNotFoundError:
66+
return device_id
67+
except Exception as e:
68+
log.warning(f"Failed to read friendly name for {dev_info_path}: {e}")
69+
return device_id
70+
71+
def read(self):
72+
"""Read all USB devices from USBSTOR and log their info."""
73+
log.info("Starting USB history extraction...")
74+
reg_path = r"SYSTEM\CurrentControlSet\Enum\USBSTOR"
75+
try:
76+
for device_class in self._enum_subkeys(winreg.HKEY_LOCAL_MACHINE, reg_path, log.warning):
77+
dev_class_path = f"{reg_path}\\{device_class}"
78+
for device_id in self._enum_subkeys(winreg.HKEY_LOCAL_MACHINE, dev_class_path, log.warning):
79+
dev_info_path = f"{dev_class_path}\\{device_id}"
80+
friendly_name = self._get_friendly_name(dev_info_path, device_id)
81+
last_write = self._get_last_write_time(winreg.HKEY_LOCAL_MACHINE, dev_info_path) or "Unknown"
82+
self._save_history(f"USB Device Found: {friendly_name} | LastWriteTime: {last_write}")
83+
log.info(f"USB history extraction complete, saved to {self.history_path}")
84+
except Exception as e:
85+
log.error(f"Error during USB history extraction: {e}")
86+
87+
88+
if __name__ == "__main__":
89+
USBHistory().read()

0 commit comments

Comments
 (0)