atomicshop 2.15.11__py3-none-any.whl → 3.10.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/strings.py +72 -3
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -3
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +11 -6
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +1 -1
- atomicshop/diff_check.py +3 -3
- atomicshop/dns.py +128 -3
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/trace.py +40 -42
- atomicshop/etws/traces/trace_dns.py +56 -44
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +27 -5
- atomicshop/file_io/docxs.py +34 -17
- atomicshop/file_io/file_io.py +31 -17
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +616 -291
- atomicshop/get_process_list.py +3 -3
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -80
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +136 -40
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
- atomicshop/monitor/checks/dns.py +1 -1
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +39 -9
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -42
- atomicshop/process.py +24 -6
- atomicshop/process_poller/process_pool.py +0 -1
- atomicshop/process_poller/simple_process_pool.py +204 -5
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/system_resource_monitor.py +61 -47
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/urls.py +6 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +98 -27
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/githubw.py +537 -54
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/filters.py +23 -0
- atomicshop/wrappers/loggingw/formatters.py +12 -0
- atomicshop/wrappers/loggingw/handlers.py +214 -107
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +860 -22
- atomicshop/wrappers/loggingw/reading.py +134 -112
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +37 -1
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +491 -182
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +1 -1
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/pycharm.py +0 -9
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
- atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/config_editor.py +0 -37
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -268
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -151
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/install.py +0 -81
- atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
- atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
- atomicshop/wrappers/msiw.py +0 -149
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/psutilw/networks.py +0 -45
- atomicshop/wrappers/pycharmw.py +0 -81
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.15.11.dist-info/RECORD +0 -302
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- /atomicshop/{archiver → permissions}/__init__.py +0 -0
- /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
- /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
from typing import List, Literal
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_interface_ips(
|
|
7
|
+
interface_name: str,
|
|
8
|
+
ip_type: Literal["virtual", "dynamic", "all"] = "virtual"
|
|
9
|
+
) -> List[str]:
|
|
10
|
+
"""
|
|
11
|
+
Return IPv4 addresses on an interface, filtered by 'mode'.
|
|
12
|
+
|
|
13
|
+
ip_type:
|
|
14
|
+
- "virtual": only static/virtual IPs (PrefixOrigin != 'Dhcp')
|
|
15
|
+
- "dynamic": only DHCP IPs (PrefixOrigin == 'Dhcp')
|
|
16
|
+
- "all": all IPv4 IPs on the interface
|
|
17
|
+
|
|
18
|
+
If the interface does not exist or has no IPv4 addresses, returns [].
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
ps_script = f"""
|
|
22
|
+
try {{
|
|
23
|
+
Get-NetIPAddress -InterfaceAlias "{interface_name}" -AddressFamily IPv4 |
|
|
24
|
+
Select-Object IPAddress,
|
|
25
|
+
@{{
|
|
26
|
+
Name = 'PrefixOrigin';
|
|
27
|
+
Expression = {{ [string]$_.PrefixOrigin }}
|
|
28
|
+
}} |
|
|
29
|
+
ConvertTo-Json -Depth 3
|
|
30
|
+
}} catch {{
|
|
31
|
+
# Return empty JSON array if nothing found / interface missing
|
|
32
|
+
'[]'
|
|
33
|
+
}}
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
result = subprocess.run(
|
|
38
|
+
["powershell", "-NoProfile", "-Command", ps_script],
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
check=True
|
|
42
|
+
)
|
|
43
|
+
except subprocess.CalledProcessError as e:
|
|
44
|
+
# If anything unexpected happens, raise a clearer error
|
|
45
|
+
msg = (e.stderr or e.stdout or "").strip()
|
|
46
|
+
raise RuntimeError(f"PowerShell Get-NetIPAddress failed: {msg}") from e
|
|
47
|
+
|
|
48
|
+
stdout = result.stdout.strip()
|
|
49
|
+
if not stdout:
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
# At this point stdout should be valid JSON (list or single object)
|
|
53
|
+
data = json.loads(stdout)
|
|
54
|
+
|
|
55
|
+
if isinstance(data, dict):
|
|
56
|
+
data = [data]
|
|
57
|
+
|
|
58
|
+
ips: List[str] = []
|
|
59
|
+
ip_type = ip_type.lower()
|
|
60
|
+
|
|
61
|
+
for entry in data:
|
|
62
|
+
ip = entry.get("IPAddress")
|
|
63
|
+
origin_raw = entry.get("PrefixOrigin", "")
|
|
64
|
+
origin = str(origin_raw).lower()
|
|
65
|
+
|
|
66
|
+
if not ip:
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
if ip_type == "virtual":
|
|
70
|
+
if origin != "dhcp":
|
|
71
|
+
ips.append(ip)
|
|
72
|
+
elif ip_type == "dynamic":
|
|
73
|
+
if origin == "dhcp":
|
|
74
|
+
ips.append(ip)
|
|
75
|
+
elif ip_type == "all":
|
|
76
|
+
ips.append(ip)
|
|
77
|
+
else:
|
|
78
|
+
raise ValueError(f"Unsupported mode: {ip_type!r}")
|
|
79
|
+
|
|
80
|
+
return ips
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import os
|
|
2
2
|
import time
|
|
3
3
|
|
|
4
|
+
import psutil
|
|
5
|
+
|
|
4
6
|
from ...print_api import print_api
|
|
7
|
+
from ..ctyping import file_details_winapi
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
def wait_for_process(pid: int):
|
|
@@ -43,3 +46,36 @@ def kill_process_by_pid(pid: int, print_kwargs: dict = None):
|
|
|
43
46
|
except psutil.TimeoutExpired:
|
|
44
47
|
# print(f"Process {pid} did not terminate in time.")
|
|
45
48
|
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_running_processes_with_exe_info() -> list[dict]:
|
|
52
|
+
"""
|
|
53
|
+
Retrieve information about all running processes on the system.
|
|
54
|
+
"""
|
|
55
|
+
processes_info: list[dict] = []
|
|
56
|
+
|
|
57
|
+
for proc in psutil.process_iter(attrs=["pid", "name", "exe"]):
|
|
58
|
+
try:
|
|
59
|
+
pid = proc.info["pid"]
|
|
60
|
+
name = proc.info["name"]
|
|
61
|
+
exe = proc.info["exe"]
|
|
62
|
+
|
|
63
|
+
if exe and os.path.isfile(exe):
|
|
64
|
+
# Get file properties
|
|
65
|
+
file_properties = file_details_winapi.get_file_properties(exe)
|
|
66
|
+
|
|
67
|
+
# Add process info to the list
|
|
68
|
+
processes_info.append({
|
|
69
|
+
"PID": pid,
|
|
70
|
+
"Name": name,
|
|
71
|
+
"FilePath": exe,
|
|
72
|
+
"FileDescription": file_properties["FileDescription"],
|
|
73
|
+
"FileVersion": file_properties["FileVersion"],
|
|
74
|
+
"ProductName": file_properties["ProductName"],
|
|
75
|
+
"ProductVersion": file_properties["ProductVersion"],
|
|
76
|
+
})
|
|
77
|
+
except (psutil.AccessDenied, psutil.NoSuchProcess, psutil.ZombieProcess):
|
|
78
|
+
# Skip processes that cannot be accessed
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
return processes_info
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import shlex
|
|
3
|
+
import socket
|
|
4
|
+
|
|
5
|
+
import psutil
|
|
6
|
+
|
|
7
|
+
from ... import networks
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_process_using_port(ip_port: str) -> Union[dict, None]:
|
|
11
|
+
"""
|
|
12
|
+
Function to find the process using the port.
|
|
13
|
+
:param ip_port: string, Listening IP and port number. Example: '192.168.0.1:443'
|
|
14
|
+
:return: dict['pid', 'name', 'cmdline'] or None.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
ip_address, port = ip_port.split(':')
|
|
18
|
+
port = int(port)
|
|
19
|
+
|
|
20
|
+
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
|
|
21
|
+
try:
|
|
22
|
+
connections = proc.connections(kind='inet')
|
|
23
|
+
for conn in connections:
|
|
24
|
+
# if conn.laddr.port == port:
|
|
25
|
+
# Status LISTEN is for TCP sockets and NONE is for UDP sockets.
|
|
26
|
+
# Sometimes after socket close, the port will be in TIME_WAIT state.
|
|
27
|
+
if (conn.laddr.port == port and (conn.status == 'LISTEN' or conn.status == 'NONE')) and conn.laddr.ip == ip_address:
|
|
28
|
+
cmdline = proc.info['cmdline']
|
|
29
|
+
if not cmdline:
|
|
30
|
+
cmdline = '<EMPTY: TRY RUNNING AS ADMIN>'
|
|
31
|
+
else:
|
|
32
|
+
cmdline = shlex.join(cmdline)
|
|
33
|
+
return {
|
|
34
|
+
'pid': proc.info['pid'],
|
|
35
|
+
'name': proc.info['name'],
|
|
36
|
+
'cmdline': cmdline
|
|
37
|
+
}
|
|
38
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
39
|
+
pass
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_processes_using_port_list(ips_ports: list) -> Union[dict, None]:
|
|
44
|
+
"""
|
|
45
|
+
Function to find the process using the port.
|
|
46
|
+
:param ips_ports: List of listening ips and port numbers. Example:
|
|
47
|
+
['192.168.0.1:443', '192.168.0.2:443']
|
|
48
|
+
:return: dict[port: {'pid', 'name', 'cmdline'}] or None.
|
|
49
|
+
"""
|
|
50
|
+
port_process_map = {}
|
|
51
|
+
for ip_port in ips_ports:
|
|
52
|
+
process_info = get_process_using_port(ip_port)
|
|
53
|
+
if process_info:
|
|
54
|
+
port_process_map[ip_port] = process_info
|
|
55
|
+
|
|
56
|
+
return port_process_map
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_default_connection_name() -> Union[dict, None]:
|
|
60
|
+
"""
|
|
61
|
+
Function to get the default network interface.
|
|
62
|
+
:return: dict[interface_name: details] or None.
|
|
63
|
+
"""
|
|
64
|
+
# Get all interfaces.
|
|
65
|
+
interfaces: dict = psutil.net_if_addrs()
|
|
66
|
+
default_ip_address: str = networks.get_default_internet_ipv4()
|
|
67
|
+
|
|
68
|
+
for interface, details in interfaces.items():
|
|
69
|
+
for address in details:
|
|
70
|
+
# Check if the address is an IPv4 address (AF_INET) and not a loopback (127.0.0.1)
|
|
71
|
+
if address.family == socket.AF_INET and not address.address.startswith('127.'):
|
|
72
|
+
# Check if the address is the default IP address.
|
|
73
|
+
if address.address == default_ip_address:
|
|
74
|
+
return {interface: details}
|
|
75
|
+
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def list_network_interfaces() -> list:
|
|
80
|
+
"""
|
|
81
|
+
Function to list all network interfaces.
|
|
82
|
+
:return: list of interface names.
|
|
83
|
+
"""
|
|
84
|
+
iface_names = list(psutil.net_if_addrs().keys())
|
|
85
|
+
return iface_names
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
from secrets import randbits
|
|
2
2
|
from OpenSSL import crypto
|
|
3
3
|
|
|
4
4
|
from .. import certificates
|
|
@@ -121,7 +121,14 @@ def generate_server_certificate_empty(
|
|
|
121
121
|
"""
|
|
122
122
|
|
|
123
123
|
cert = crypto.X509()
|
|
124
|
-
|
|
124
|
+
|
|
125
|
+
# RFC 5280: serial must be positive and non-zero
|
|
126
|
+
# 1 … 2⁶⁴-1
|
|
127
|
+
cert.set_serial_number(randbits(64))
|
|
128
|
+
current_serial = cert.get_serial_number()
|
|
129
|
+
if current_serial <= 0:
|
|
130
|
+
raise RuntimeError(f"Refusing to create a certificate with non-positive serial: {str(current_serial)}")
|
|
131
|
+
|
|
125
132
|
cert.get_subject().CN = certname
|
|
126
133
|
|
|
127
134
|
cert.set_version(2)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
import win32crypt as wcrypt
|
|
3
|
+
|
|
4
|
+
from ...print_api import print_api
|
|
5
|
+
from ... import certificates
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# lpszStoreProvider
|
|
9
|
+
CERT_STORE_PROV_SYSTEM = 0x0000000A
|
|
10
|
+
# dwFlags
|
|
11
|
+
CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
|
|
12
|
+
CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
|
|
13
|
+
CERT_CLOSE_STORE_FORCE_FLAG = 0x00000001
|
|
14
|
+
CRYPT_STRING_BASE64HEADER = 0x00000000
|
|
15
|
+
X509_ASN_ENCODING = 0x00000001
|
|
16
|
+
CERT_STORE_ADD_REPLACE_EXISTING = 3
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
STORE_LOCATION_TO_CERT_SYSTEM_STORE: dict = {
|
|
20
|
+
"ROOT": CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
21
|
+
"CA": CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
22
|
+
"MY": CERT_SYSTEM_STORE_CURRENT_USER
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def delete_certificate_by_issuer_name(
|
|
27
|
+
issuer_name: str,
|
|
28
|
+
store_location: Literal[
|
|
29
|
+
"ROOT",
|
|
30
|
+
"CA",
|
|
31
|
+
"MY"] = "ROOT",
|
|
32
|
+
print_kwargs: dict = None
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
NEED ADMIN RIGHTS.
|
|
36
|
+
The function will remove all certificates with the specified issuer name.
|
|
37
|
+
There can be several certificates with this name.
|
|
38
|
+
|
|
39
|
+
:param issuer_name: string, issuer name to search for.
|
|
40
|
+
:param store_location: string, store location to search in. Default is "ROOT".
|
|
41
|
+
:param print_kwargs: dict, print_api kwargs.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
WinAPI doesn't like to do 2 actions in one iteration. So, first we will collect all certificates to remove,
|
|
46
|
+
and in the second iteration remove them.
|
|
47
|
+
|
|
48
|
+
Full Explanation:
|
|
49
|
+
When you iterate with for cert in store.CertEnumCertificatesInStore(): and call
|
|
50
|
+
cert.CertDeleteCertificateFromStore() inside that loop, you’re modifying the underlying certificate store
|
|
51
|
+
while its internal enumeration is still active. This can lead to a segmentation fault (access violation 0xC0000005).
|
|
52
|
+
By collecting the certificates in the first pass, you freeze the iteration so the store
|
|
53
|
+
doesn’t get mutated mid-enumeration.
|
|
54
|
+
In the second pass, when you actually remove them, you’re no longer in the middle of enumerating.
|
|
55
|
+
This prevents the store’s pointer from becoming invalid.
|
|
56
|
+
|
|
57
|
+
This approach should stop the Process finished with exit code -1073741819 (0xC0000005) issue.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
store = wcrypt.CertOpenStore(
|
|
61
|
+
CERT_STORE_PROV_SYSTEM,
|
|
62
|
+
0,
|
|
63
|
+
None,
|
|
64
|
+
STORE_LOCATION_TO_CERT_SYSTEM_STORE[store_location],
|
|
65
|
+
store_location
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Collect all matching certificates in a list
|
|
69
|
+
certs_to_remove = []
|
|
70
|
+
for cert in store.CertEnumCertificatesInStore():
|
|
71
|
+
# Certificate properties.
|
|
72
|
+
# cert.CertEnumCertificateContextProperties()
|
|
73
|
+
subject_string: str = wcrypt.CertNameToStr(cert.Subject)
|
|
74
|
+
if subject_string == issuer_name:
|
|
75
|
+
# Remove the certificate.
|
|
76
|
+
certs_to_remove.append(cert)
|
|
77
|
+
|
|
78
|
+
# Remove all certificates from the list.
|
|
79
|
+
for cert in certs_to_remove:
|
|
80
|
+
cert.CertDeleteCertificateFromStore()
|
|
81
|
+
print_api(f"Removed the Certificate from store [{store_location}] with issuer [{issuer_name}]", **(print_kwargs or {}))
|
|
82
|
+
|
|
83
|
+
# There is an exception about store close.
|
|
84
|
+
# store.CertCloseStore()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def install_certificate_file(
|
|
88
|
+
file_path: str,
|
|
89
|
+
store_location: Literal[
|
|
90
|
+
"ROOT", "CA", "MY"] = "ROOT",
|
|
91
|
+
print_kwargs: dict = None
|
|
92
|
+
):
|
|
93
|
+
"""
|
|
94
|
+
NEED ADMIN RIGHTS.
|
|
95
|
+
The function will install the certificate from the file to the specified store location.
|
|
96
|
+
|
|
97
|
+
:param file_path: string, full file path to the certificate file.
|
|
98
|
+
:param store_location: string, store location to install the certificate. Default is "ROOT".
|
|
99
|
+
:param print_kwargs: dict, print_api kwargs.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
with open(file_path, 'r') as f:
|
|
103
|
+
certificate_string = f.read()
|
|
104
|
+
|
|
105
|
+
certificate_pem = certificates.get_pem_certificate_from_string(certificate_string)
|
|
106
|
+
|
|
107
|
+
certificate_bytes = wcrypt.CryptStringToBinary(certificate_pem, CRYPT_STRING_BASE64HEADER)[0]
|
|
108
|
+
|
|
109
|
+
store = wcrypt.CertOpenStore(
|
|
110
|
+
CERT_STORE_PROV_SYSTEM, 0, None, STORE_LOCATION_TO_CERT_SYSTEM_STORE[store_location], store_location)
|
|
111
|
+
|
|
112
|
+
store.CertAddEncodedCertificateToStore(X509_ASN_ENCODING, certificate_bytes, CERT_STORE_ADD_REPLACE_EXISTING)
|
|
113
|
+
store.CertCloseStore(CERT_CLOSE_STORE_FORCE_FLAG)
|
|
114
|
+
|
|
115
|
+
message = f"Certificate installed to the store: [{store_location}] from file: [{file_path}]"
|
|
116
|
+
print_api(message, **(print_kwargs or {}))
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import win32evtlog
|
|
2
|
+
import win32security
|
|
3
|
+
import win32con
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_latest_events(
|
|
7
|
+
server_ip: str = ".",
|
|
8
|
+
username: str = None,
|
|
9
|
+
password: str = None,
|
|
10
|
+
domain: str = ".",
|
|
11
|
+
log_name: str = "Security",
|
|
12
|
+
count: int = None,
|
|
13
|
+
event_id_list: list[int] | None = None
|
|
14
|
+
):
|
|
15
|
+
"""
|
|
16
|
+
Fetch latest `count` events from a Windows Event Log (local or remote) using pywin32.
|
|
17
|
+
|
|
18
|
+
- If username/password are None => use current security context.
|
|
19
|
+
- If server_ip is ".", "localhost", "127.0.0.1", "" or None => open local log.
|
|
20
|
+
- If count is None => return *all* events in the log.
|
|
21
|
+
- If event_id_list is not None => only return events whose EventID is in that list.
|
|
22
|
+
|
|
23
|
+
:param server_ip: IPv4/hostname of remote machine, or "." / None for local
|
|
24
|
+
:param username: Username to authenticate with (optional)
|
|
25
|
+
:param password: Password for the user (optional)
|
|
26
|
+
:param domain: Domain or computer name; ignored if username is None.
|
|
27
|
+
If None and username is given, "." is used.
|
|
28
|
+
:param log_name: Log name (e.g. "Security", "System", "Application")
|
|
29
|
+
:param count: Number of most recent events to return, or None for all
|
|
30
|
+
:param event_id_list: List of Event IDs (low 16-bit) to include, or None for all
|
|
31
|
+
:return: List of dicts describing events (most recent first)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# Normalize server for OpenEventLog: None means "local machine"
|
|
35
|
+
normalized_server = server_ip
|
|
36
|
+
if server_ip in (None, "", ".", "localhost", "127.0.0.1"):
|
|
37
|
+
normalized_server = None
|
|
38
|
+
|
|
39
|
+
# Max events logic: None => infinite
|
|
40
|
+
max_events = float("inf") if count is None else count
|
|
41
|
+
|
|
42
|
+
# Precompute set of event IDs for fast membership checks
|
|
43
|
+
event_ids_set = set(event_id_list) if event_id_list is not None else None
|
|
44
|
+
|
|
45
|
+
# Decide whether we need impersonation
|
|
46
|
+
use_impersonation = username is not None and password is not None
|
|
47
|
+
|
|
48
|
+
h_user = None
|
|
49
|
+
events = []
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
if use_impersonation:
|
|
53
|
+
if domain is None:
|
|
54
|
+
domain = "."
|
|
55
|
+
|
|
56
|
+
# Log on with explicit credentials and impersonate for the remote call
|
|
57
|
+
# LOGON32_LOGON_NEW_CREDENTIALS lets us use these creds for remote access
|
|
58
|
+
# while keeping the local token mostly unchanged.
|
|
59
|
+
h_user = win32security.LogonUser(
|
|
60
|
+
username,
|
|
61
|
+
domain,
|
|
62
|
+
password,
|
|
63
|
+
win32con.LOGON32_LOGON_NEW_CREDENTIALS,
|
|
64
|
+
win32con.LOGON32_PROVIDER_WINNT50,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
win32security.ImpersonateLoggedOnUser(h_user)
|
|
68
|
+
|
|
69
|
+
# Connect to remote event log
|
|
70
|
+
# `server_ip` can be an IP or hostname; no need for leading "\\".
|
|
71
|
+
# local if normalized_server is None.
|
|
72
|
+
h_log = win32evtlog.OpenEventLog(normalized_server, log_name)
|
|
73
|
+
|
|
74
|
+
flags = (
|
|
75
|
+
win32evtlog.EVENTLOG_BACKWARDS_READ
|
|
76
|
+
| win32evtlog.EVENTLOG_SEQUENTIAL_READ
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
offset = 0 # not used with BACKWARDS_READ + SEQUENTIAL_READ, but kept for clarity
|
|
80
|
+
|
|
81
|
+
while len(events) < max_events:
|
|
82
|
+
records = win32evtlog.ReadEventLog(h_log, flags, offset)
|
|
83
|
+
if not records:
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
for ev in records:
|
|
87
|
+
# Low 16 bits are the actual Event ID
|
|
88
|
+
eid = ev.EventID & 0xFFFF
|
|
89
|
+
|
|
90
|
+
# If filtering by event IDs, skip others
|
|
91
|
+
if event_ids_set is not None and eid not in event_ids_set:
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
raw_strings = list(ev.StringInserts or [])
|
|
95
|
+
strings_dict = _parse_strings(eid, ev.SourceName, raw_strings)
|
|
96
|
+
|
|
97
|
+
evt = {
|
|
98
|
+
"RecordNumber": ev.RecordNumber,
|
|
99
|
+
"TimeGenerated": ev.TimeGenerated.Format(), # string time
|
|
100
|
+
"ComputerName": ev.ComputerName,
|
|
101
|
+
"SourceName": ev.SourceName,
|
|
102
|
+
# Low 16 bits are the actual Event ID
|
|
103
|
+
"EventID": ev.EventID & 0xFFFF,
|
|
104
|
+
"EventType": ev.EventType,
|
|
105
|
+
"EventCategory": ev.EventCategory,
|
|
106
|
+
"Strings": raw_strings,
|
|
107
|
+
"StringsDict": strings_dict,
|
|
108
|
+
}
|
|
109
|
+
events.append(evt)
|
|
110
|
+
|
|
111
|
+
if len(events) >= max_events:
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
win32evtlog.CloseEventLog(h_log)
|
|
115
|
+
|
|
116
|
+
finally:
|
|
117
|
+
# Clean up impersonation if we used it, Always revert impersonation and close handle
|
|
118
|
+
if use_impersonation and h_user is not None:
|
|
119
|
+
win32security.RevertToSelf()
|
|
120
|
+
h_user.Close()
|
|
121
|
+
|
|
122
|
+
# `events` is in newest to oldest already because of BACKWARDS_READ.
|
|
123
|
+
return events
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _parse_strings(event_id: int, source_name: str, strings: list[str]) -> dict:
|
|
127
|
+
"""
|
|
128
|
+
Convert the raw 'Strings' list into a dictionary with friendly field names
|
|
129
|
+
for specific event IDs. For unknown events, fall back to String1, String2, ...
|
|
130
|
+
|
|
131
|
+
Currently has a special mapping for:
|
|
132
|
+
- 5156 (Security log, WFP allowed connection)
|
|
133
|
+
"""
|
|
134
|
+
if not strings:
|
|
135
|
+
return {}
|
|
136
|
+
|
|
137
|
+
# Normalize source name a bit
|
|
138
|
+
src = (source_name or "").lower()
|
|
139
|
+
|
|
140
|
+
# Special-case: Security 5156 (Windows Filtering Platform has permitted a connection)
|
|
141
|
+
# Insertion strings (in order) are:
|
|
142
|
+
# 1 Process ID
|
|
143
|
+
# 2 Application Name
|
|
144
|
+
# 3 Direction
|
|
145
|
+
# 4 Source Address
|
|
146
|
+
# 5 Source Port
|
|
147
|
+
# 6 Destination Address
|
|
148
|
+
# 7 Destination Port
|
|
149
|
+
# 8 Protocol
|
|
150
|
+
# 9 Filter Run-Time ID
|
|
151
|
+
# 10 Layer Name
|
|
152
|
+
# 11 Layer Run-Time ID
|
|
153
|
+
if event_id == 5156 and "security-auditing" in src:
|
|
154
|
+
keys_5156 = [
|
|
155
|
+
"Process ID",
|
|
156
|
+
"Application Name",
|
|
157
|
+
"Direction",
|
|
158
|
+
"Source Address",
|
|
159
|
+
"Source Port",
|
|
160
|
+
"Destination Address",
|
|
161
|
+
"Destination Port",
|
|
162
|
+
"Protocol",
|
|
163
|
+
"Filter Run-Time ID",
|
|
164
|
+
"Layer Name",
|
|
165
|
+
"Layer Run-Time ID",
|
|
166
|
+
]
|
|
167
|
+
return {
|
|
168
|
+
key: strings[i]
|
|
169
|
+
for i, key in enumerate(keys_5156)
|
|
170
|
+
if i < len(strings)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Default: generic mapping
|
|
174
|
+
return {f"String{i+1}": s for i, s in enumerate(strings)}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
import winreg
|
|
3
|
-
|
|
4
1
|
from .. import subscribe
|
|
5
|
-
from
|
|
2
|
+
from .... import win_auditw
|
|
6
3
|
|
|
7
4
|
|
|
8
|
-
AUDITING_REG_PATH: str = r"Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit"
|
|
9
|
-
PROCESS_CREATION_INCLUDE_CMDLINE_VALUE: str = "ProcessCreationIncludeCmdLine_Enabled"
|
|
10
5
|
LOG_CHANNEL: str = 'Security'
|
|
11
6
|
EVENT_ID: int = 4688
|
|
12
7
|
|
|
@@ -30,8 +25,8 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
|
|
|
30
25
|
|
|
31
26
|
def start(self):
|
|
32
27
|
"""Start the subscription process."""
|
|
33
|
-
enable_audit_process_creation()
|
|
34
|
-
enable_command_line_auditing()
|
|
28
|
+
win_auditw.enable_audit_process_creation()
|
|
29
|
+
win_auditw.enable_command_line_auditing()
|
|
35
30
|
|
|
36
31
|
super().start()
|
|
37
32
|
|
|
@@ -60,100 +55,3 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
|
|
|
60
55
|
# print(f"Error looking up account SID: {e}")
|
|
61
56
|
|
|
62
57
|
return data
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def enable_audit_process_creation(print_kwargs: dict = None):
|
|
66
|
-
"""
|
|
67
|
-
Enable the 'Audit Process Creation' policy.
|
|
68
|
-
|
|
69
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
70
|
-
"""
|
|
71
|
-
if is_audit_process_creation_enabled():
|
|
72
|
-
print_api("Audit Process Creation is already enabled.", color='yellow', **(print_kwargs or {}))
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
# Enable "Audit Process Creation" policy
|
|
76
|
-
audit_policy_command = [
|
|
77
|
-
"auditpol", "/set", "/subcategory:Process Creation", "/success:enable", "/failure:enable"
|
|
78
|
-
]
|
|
79
|
-
try:
|
|
80
|
-
subprocess.run(audit_policy_command, check=True)
|
|
81
|
-
print_api("Successfully enabled 'Audit Process Creation'.", color='green', **(print_kwargs or {}))
|
|
82
|
-
except subprocess.CalledProcessError as e:
|
|
83
|
-
print_api(f"Failed to enable 'Audit Process Creation': {e}", error_type=True, color='red', **(print_kwargs or {}))
|
|
84
|
-
raise e
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def is_audit_process_creation_enabled(print_kwargs: dict = None) -> bool:
|
|
88
|
-
"""
|
|
89
|
-
Check if the 'Audit Process Creation' policy is enabled.
|
|
90
|
-
|
|
91
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
92
|
-
"""
|
|
93
|
-
# Command to check the "Audit Process Creation" policy
|
|
94
|
-
audit_policy_check_command = [
|
|
95
|
-
"auditpol", "/get", "/subcategory:Process Creation"
|
|
96
|
-
]
|
|
97
|
-
try:
|
|
98
|
-
result = subprocess.run(audit_policy_check_command, check=True, capture_output=True, text=True)
|
|
99
|
-
output = result.stdout
|
|
100
|
-
# print_api(output) # Print the output for inspection
|
|
101
|
-
|
|
102
|
-
if "Process Creation" in output and "Success and Failure" in output:
|
|
103
|
-
# print_api(
|
|
104
|
-
# "'Audit Process Creation' is enabled for both success and failure.",
|
|
105
|
-
# color='green', **(print_kwargs or {}))
|
|
106
|
-
return True
|
|
107
|
-
else:
|
|
108
|
-
# print_api(output, **(print_kwargs or {}))
|
|
109
|
-
# print_api(
|
|
110
|
-
# "'Audit Process Creation' is not fully enabled. Check the output above for details.",
|
|
111
|
-
# color='yellow', **(print_kwargs or {}))
|
|
112
|
-
return False
|
|
113
|
-
except subprocess.CalledProcessError as e:
|
|
114
|
-
print_api(f"Failed to check 'Audit Process Creation': {e}", color='red', error_type=True, **(print_kwargs or {}))
|
|
115
|
-
return False
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def enable_command_line_auditing(print_kwargs: dict = None):
|
|
119
|
-
"""
|
|
120
|
-
Enable the 'Include command line in process creation events' policy.
|
|
121
|
-
|
|
122
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
123
|
-
"""
|
|
124
|
-
|
|
125
|
-
if is_command_line_auditing_enabled():
|
|
126
|
-
print_api(
|
|
127
|
-
"'Include command line in process creation events' is already enabled.", color='yellow',
|
|
128
|
-
**(print_kwargs or {}))
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
try:
|
|
132
|
-
# Open the registry key
|
|
133
|
-
with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH) as reg_key:
|
|
134
|
-
# Set the value
|
|
135
|
-
winreg.SetValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE, 0, winreg.REG_DWORD, 1)
|
|
136
|
-
|
|
137
|
-
print_api(
|
|
138
|
-
"Successfully enabled 'Include command line in process creation events'.",
|
|
139
|
-
color='green', **(print_kwargs or {}))
|
|
140
|
-
except WindowsError as e:
|
|
141
|
-
print_api(
|
|
142
|
-
f"Failed to enable 'Include command line in process creation events': {e}", error_type=True,
|
|
143
|
-
color='red', **(print_kwargs or {}))
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def is_command_line_auditing_enabled():
|
|
147
|
-
try:
|
|
148
|
-
# Open the registry key
|
|
149
|
-
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH, 0, winreg.KEY_READ) as reg_key:
|
|
150
|
-
# Query the value
|
|
151
|
-
value, reg_type = winreg.QueryValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE)
|
|
152
|
-
# Check if the value is 1 (enabled)
|
|
153
|
-
return value == 1
|
|
154
|
-
except FileNotFoundError:
|
|
155
|
-
# Key or value not found, assume it's not enabled
|
|
156
|
-
return False
|
|
157
|
-
except WindowsError as e:
|
|
158
|
-
print(f"Failed to read the 'Include command line in process creation events' setting: {e}")
|
|
159
|
-
return False
|