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
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
|
|
3
1
|
from .. import subscribe
|
|
4
|
-
from
|
|
2
|
+
from .... import win_auditw
|
|
5
3
|
|
|
6
4
|
|
|
7
5
|
LOG_CHANNEL: str = 'Security'
|
|
@@ -27,7 +25,7 @@ class ProcessTerminateSubscriber(subscribe.EventLogSubscriber):
|
|
|
27
25
|
|
|
28
26
|
def start(self):
|
|
29
27
|
"""Start the subscription process."""
|
|
30
|
-
enable_audit_process_termination()
|
|
28
|
+
win_auditw.enable_audit_process_termination()
|
|
31
29
|
|
|
32
30
|
super().start()
|
|
33
31
|
|
|
@@ -48,56 +46,4 @@ class ProcessTerminateSubscriber(subscribe.EventLogSubscriber):
|
|
|
48
46
|
|
|
49
47
|
data['ProcessIdInt'] = int(data['ProcessId'], 16)
|
|
50
48
|
|
|
51
|
-
return data
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def enable_audit_process_termination(print_kwargs: dict = None):
|
|
55
|
-
"""
|
|
56
|
-
Enable the 'Audit Process Termination' policy.
|
|
57
|
-
|
|
58
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
59
|
-
"""
|
|
60
|
-
if is_audit_process_termination_enabled():
|
|
61
|
-
print_api("Audit Process Termination is already enabled.", color='yellow', **(print_kwargs or {}))
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
audit_policy_command = [
|
|
65
|
-
"auditpol", "/set", "/subcategory:Process Termination", "/success:enable", "/failure:enable"
|
|
66
|
-
]
|
|
67
|
-
try:
|
|
68
|
-
subprocess.run(audit_policy_command, check=True)
|
|
69
|
-
print_api("Successfully enabled 'Audit Process Termination'.", color='green', **(print_kwargs or {}))
|
|
70
|
-
except subprocess.CalledProcessError as e:
|
|
71
|
-
print_api(f"Failed to enable 'Audit Process Termination': {e}", error_type=True, color='red', **(print_kwargs or {}))
|
|
72
|
-
raise e
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def is_audit_process_termination_enabled(print_kwargs: dict = None) -> bool:
|
|
76
|
-
"""
|
|
77
|
-
Check if the 'Audit Process Termination' policy is enabled.
|
|
78
|
-
|
|
79
|
-
:param print_kwargs: Optional keyword arguments for the print function.
|
|
80
|
-
"""
|
|
81
|
-
# Command to check the "Audit Process Creation" policy
|
|
82
|
-
audit_policy_check_command = [
|
|
83
|
-
"auditpol", "/get", "/subcategory:Process Termination"
|
|
84
|
-
]
|
|
85
|
-
try:
|
|
86
|
-
result = subprocess.run(audit_policy_check_command, check=True, capture_output=True, text=True)
|
|
87
|
-
output = result.stdout
|
|
88
|
-
# print_api(output) # Print the output for inspection
|
|
89
|
-
|
|
90
|
-
if "Process Termination" in output and "Success and Failure" in output:
|
|
91
|
-
# print_api(
|
|
92
|
-
# "'Audit Process Termination' is enabled for both success and failure.",
|
|
93
|
-
# color='green', **(print_kwargs or {}))
|
|
94
|
-
return True
|
|
95
|
-
else:
|
|
96
|
-
# print_api(output, **(print_kwargs or {}))
|
|
97
|
-
# print_api(
|
|
98
|
-
# "'Audit Process Termination' is not fully enabled. Check the output above for details.",
|
|
99
|
-
# color='yellow', **(print_kwargs or {}))
|
|
100
|
-
return False
|
|
101
|
-
except subprocess.CalledProcessError as e:
|
|
102
|
-
print_api(f"Failed to check 'Audit Process Termination': {e}", color='red', error_type=True, **(print_kwargs or {}))
|
|
103
|
-
return False
|
|
49
|
+
return data
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from win32com.client import CDispatch
|
|
4
|
+
|
|
5
|
+
from . import wmi_helpers
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def set_skip_as_source(
|
|
9
|
+
ip_addresses: list[str],
|
|
10
|
+
enable: bool = True,
|
|
11
|
+
wmi_instance: CDispatch = None
|
|
12
|
+
) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Toggle SkipAsSource for every address in *ip_addrs*.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
ip_addresses : list/tuple/iterable of str
|
|
19
|
+
One or more literal IP strings, e.g. "192.168.157.3"
|
|
20
|
+
enable : bool
|
|
21
|
+
True → behave like Set‑NetIPAddress ‑SkipAsSource $true
|
|
22
|
+
False → behave like Set‑NetIPAddress ‑SkipAsSource $false
|
|
23
|
+
wmi_instance : CDispatch
|
|
24
|
+
WMI instance to use. If not provided, a new one will be created.
|
|
25
|
+
'root\\StandardCimv2'
|
|
26
|
+
|
|
27
|
+
================
|
|
28
|
+
|
|
29
|
+
Explanation.
|
|
30
|
+
When you add extra IPv4 addresses to the same NIC, Windows treats them all as “first‑class” unless you tell it otherwise.
|
|
31
|
+
Because the two new addresses (192.168.157.3 and .4) are numerically lower than the original one (.129), the TCP/IP stack now prefers one of those lower addresses as the default source for any socket whose program didn’t bind an explicit local IP.
|
|
32
|
+
|
|
33
|
+
What that looks like on the wire
|
|
34
|
+
Client sends SYN → 192.168.157.3 (or .4).
|
|
35
|
+
– Server replies with SYN/ACK ←192.168.157.3 → handshake completes, HTTP works.
|
|
36
|
+
|
|
37
|
+
Client sends SYN → 192.168.157.129.
|
|
38
|
+
– Stack still picks .3 as its favourite and answers SYN/ACK ← 192.168.157.3.
|
|
39
|
+
– Client discards the packet (wrong IP), retransmits the SYN, your code’s accept() wakes up again, and you see an “infinite accept loop”.
|
|
40
|
+
|
|
41
|
+
The flag that fixes it: SkipAsSource
|
|
42
|
+
Tell Windows not to use the extra addresses unless an application asks for them.
|
|
43
|
+
|
|
44
|
+
PowerShell.
|
|
45
|
+
# One‑off: mark the addresses you already added
|
|
46
|
+
Get-NetIPAddress -IPAddress 192.168.157.3 | Set-NetIPAddress -SkipAsSource $true
|
|
47
|
+
Get-NetIPAddress -IPAddress 192.168.157.4 | Set-NetIPAddress -SkipAsSource $true
|
|
48
|
+
|
|
49
|
+
# —OR— add new addresses the right way from the start
|
|
50
|
+
New-NetIPAddress -InterfaceAlias "Ethernet0" `
|
|
51
|
+
-IPAddress 192.168.157.3 `
|
|
52
|
+
-PrefixLength 24 `
|
|
53
|
+
-SkipAsSource $true
|
|
54
|
+
SkipAsSource = $true keeps the address fully routable for incoming traffic and lets programs bind to it explicitly.
|
|
55
|
+
|
|
56
|
+
Windows will never choose that address as the source of an outgoing packet unless the program bound the socket to it.
|
|
57
|
+
|
|
58
|
+
After you flip the flag (no reboot required) the three‑way handshake is symmetrical again and the endless accept() loop disappears.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
if not wmi_instance:
|
|
62
|
+
wmi_instance, _ = wmi_helpers.get_wmi_instance(namespace='root\\StandardCimv2')
|
|
63
|
+
|
|
64
|
+
for ip in ip_addresses:
|
|
65
|
+
query = f"SELECT * FROM MSFT_NetIPAddress WHERE IPAddress='{ip}'"
|
|
66
|
+
matches = wmi_instance.ExecQuery(query)
|
|
67
|
+
if not matches:
|
|
68
|
+
print(f"[!] {ip}: no such address found")
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
for obj in matches: # usually just one
|
|
72
|
+
if bool(obj.SkipAsSource) == enable:
|
|
73
|
+
print(f"[=] {ip}: SkipAsSource already {enable}")
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
obj.SkipAsSource = enable
|
|
77
|
+
obj.Put_() # commit the change
|
|
78
|
+
print(f"[+] {ip}: SkipAsSource set to {enable}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def is_skip_as_source(
|
|
82
|
+
ip_address: str,
|
|
83
|
+
wmi_instance: CDispatch = None
|
|
84
|
+
) -> Optional[bool]:
|
|
85
|
+
"""
|
|
86
|
+
Check whether *ip_address* currently has SkipAsSource set.
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
True – the flag is enabled
|
|
91
|
+
False – the flag is disabled
|
|
92
|
+
None – no MSFT_NetIPAddress object matches the IP (not present)
|
|
93
|
+
|
|
94
|
+
Notes
|
|
95
|
+
-----
|
|
96
|
+
* Works for both IPv4/IPv6.
|
|
97
|
+
* Uses the same Win32 CIM class (root\\StandardCimv2 › MSFT_NetIPAddress).
|
|
98
|
+
* You can pass an existing `wmi_instance` to avoid reconnecting in tight loops.
|
|
99
|
+
"""
|
|
100
|
+
# Get a WMI connection if the caller didn’t hand us one
|
|
101
|
+
if not wmi_instance:
|
|
102
|
+
wmi_instance, _ = wmi_helpers.get_wmi_instance(namespace='root\\StandardCimv2')
|
|
103
|
+
|
|
104
|
+
query = f"SELECT SkipAsSource FROM MSFT_NetIPAddress WHERE IPAddress='{ip_address}'"
|
|
105
|
+
matches = wmi_instance.ExecQuery(query)
|
|
106
|
+
|
|
107
|
+
if not matches: # address not configured on this host/NIC
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
# There should be only one entry per literal IP, but handle duplicates sanely
|
|
111
|
+
# Return True if *any* matching record has SkipAsSource = True,
|
|
112
|
+
# otherwise False (all are False).
|
|
113
|
+
return any(bool(obj.SkipAsSource) for obj in matches)
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import socket
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from win32com.client import CDispatch
|
|
6
|
+
import pywintypes
|
|
7
|
+
|
|
8
|
+
from . import wmi_helpers, win32networkadapter
|
|
9
|
+
from .... import ip_addresses
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_network_configuration_by_adapter(
|
|
13
|
+
adapter: CDispatch,
|
|
14
|
+
wmi_instance: CDispatch = None
|
|
15
|
+
) -> Union[CDispatch, None]:
|
|
16
|
+
"""
|
|
17
|
+
Get the network configuration for a specific adapter index.
|
|
18
|
+
|
|
19
|
+
:param adapter: CDispatch, Win32_NetworkAdapter object.
|
|
20
|
+
:param wmi_instance: WMI instance. You can get it from:
|
|
21
|
+
wmi_helpers.get_wmi_instance()
|
|
22
|
+
:return: Win32_NetworkAdapterConfiguration object.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
network_config: CDispatch = wmi_instance.ExecQuery(
|
|
26
|
+
f"SELECT * FROM Win32_NetworkAdapterConfiguration WHERE Index={adapter.Index}")[0]
|
|
27
|
+
|
|
28
|
+
return network_config
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_adapter_network_configuration(
|
|
32
|
+
interface_name: str = None,
|
|
33
|
+
mac_address: str = None,
|
|
34
|
+
wmi_instance: CDispatch = None
|
|
35
|
+
) -> tuple:
|
|
36
|
+
"""
|
|
37
|
+
Get the WMI network configuration for a network adapter.
|
|
38
|
+
:param interface_name: string, adapter name as shown in the network settings.
|
|
39
|
+
:param mac_address: string, MAC address of the adapter. Format: '00:00:00:00:00:00'.
|
|
40
|
+
:param wmi_instance: WMI instance. You can get it from:
|
|
41
|
+
wrappers.pywin32s.wmis.wmi_helpers.get_wmi_instance()
|
|
42
|
+
or default will be used.
|
|
43
|
+
:return: tuple(Win32_NetworkAdapterConfiguration, Win32_NetworkAdapter)
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
if not wmi_instance:
|
|
47
|
+
wmi_instance, _ = wmi_helpers.get_wmi_instance()
|
|
48
|
+
|
|
49
|
+
adapters = win32networkadapter.list_network_adapters(wmi_instance)
|
|
50
|
+
|
|
51
|
+
if interface_name is None and mac_address is None:
|
|
52
|
+
raise ValueError("Either 'interface_name' or 'mac_address' must be provided.")
|
|
53
|
+
elif interface_name and mac_address:
|
|
54
|
+
raise ValueError("Only one of 'interface_name' or 'mac_address' must be provided.")
|
|
55
|
+
|
|
56
|
+
current_adapter = None
|
|
57
|
+
if interface_name:
|
|
58
|
+
for adapter in adapters:
|
|
59
|
+
if interface_name == adapter.NetConnectionID:
|
|
60
|
+
current_adapter = adapter
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
if not current_adapter:
|
|
64
|
+
raise wmi_helpers.WMINetworkAdapterNotFoundError(f"Adapter with interface name '{interface_name}' not found.")
|
|
65
|
+
elif mac_address:
|
|
66
|
+
for adapter in adapters:
|
|
67
|
+
if mac_address == adapter.MACAddress:
|
|
68
|
+
current_adapter = adapter
|
|
69
|
+
break
|
|
70
|
+
|
|
71
|
+
if current_adapter is None:
|
|
72
|
+
raise wmi_helpers.WMINetworkAdapterNotFoundError(f"Adapter with MAC address '{mac_address}' not found.")
|
|
73
|
+
|
|
74
|
+
# Query the network adapter configurations
|
|
75
|
+
query = f"SELECT * FROM Win32_NetworkAdapterConfiguration WHERE Index='{current_adapter.DeviceID}'"
|
|
76
|
+
adapter_configs = wmi_instance.ExecQuery(query)
|
|
77
|
+
|
|
78
|
+
# Check if the adapter exists
|
|
79
|
+
if len(adapter_configs) == 0:
|
|
80
|
+
raise wmi_helpers.WMINetworkAdapterNotFoundError(f"Adapter with interface name '{interface_name}' not found.")
|
|
81
|
+
|
|
82
|
+
return adapter_configs[0], current_adapter
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def set_static_ips(
|
|
86
|
+
network_config: CDispatch, # Win32_NetworkAdapterConfiguration (CDispatch)
|
|
87
|
+
ips: list[str], # ["192.168.157.3", ...]
|
|
88
|
+
masks: list[str], # ["255.255.255.0", ...]
|
|
89
|
+
gateways: list[str] = None,
|
|
90
|
+
dns_gateways: list[str] = None,
|
|
91
|
+
availability_wait_seconds: int = 0
|
|
92
|
+
) -> None:
|
|
93
|
+
"""
|
|
94
|
+
• network_config – Win32_NetworkAdapterConfiguration instance for the target NIC
|
|
95
|
+
(you already have it from GetObject / WMI query).
|
|
96
|
+
• ips – list of IPv4 strings.
|
|
97
|
+
• masks – matching subnet‑mask list (same length as ipv4).
|
|
98
|
+
• gateways – list of default gateways (optional).
|
|
99
|
+
• dns_gateways – list of DNS servers (optional).
|
|
100
|
+
• availability_wait_seconds – seconds to wait for the adapter to become available.
|
|
101
|
+
0 means no wait.
|
|
102
|
+
|
|
103
|
+
Raises RuntimeError if Windows reports anything other than success (0 / 1)
|
|
104
|
+
or "object already exists" (22) for each operation.
|
|
105
|
+
|
|
106
|
+
==========
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
cfg = wmi_instance.Get("Win32_NetworkAdapterConfiguration.Index=12") # your adapter
|
|
110
|
+
set_static_ips(
|
|
111
|
+
cfg,
|
|
112
|
+
ipv4=["192.168.157.129", "192.168.157.3", "192.168.157.4"],
|
|
113
|
+
masks=["255.255.255.0"] * 3,
|
|
114
|
+
# gateways=["192.168.157.2"],
|
|
115
|
+
# dns_gateways=["8.8.8.8", "1.1.1.1"]
|
|
116
|
+
)
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
initial_default_ipv4: str = socket.gethostbyname(socket.gethostname())
|
|
120
|
+
|
|
121
|
+
# -------------------- IPv4 via EnableStatic ----------------------------
|
|
122
|
+
if not masks or len(ips) != len(masks):
|
|
123
|
+
raise ValueError("ipv4 and masks must be lists of equal length")
|
|
124
|
+
|
|
125
|
+
# # refresh the instance – state may have changed after the previous call
|
|
126
|
+
# network_config = network_config.Path_.GetObject_()
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
in_params = network_config.Methods_("EnableStatic").InParameters.SpawnInstance_()
|
|
130
|
+
except pywintypes.com_error as e:
|
|
131
|
+
if e.excepinfo[1] == 'SWbemMethodSet' and 'Not found' in e.excepinfo[2]:
|
|
132
|
+
raise RuntimeError(f"Probably the adapter is already set to static non-DHCP.\n"
|
|
133
|
+
f"Failed to get [EnableStatic] method parameters: {e}\n")
|
|
134
|
+
else:
|
|
135
|
+
raise
|
|
136
|
+
|
|
137
|
+
in_params.IPAddress = ips
|
|
138
|
+
in_params.SubnetMask = masks
|
|
139
|
+
|
|
140
|
+
rc = network_config.ExecMethod_("EnableStatic", in_params).Properties_('ReturnValue').Value
|
|
141
|
+
if rc not in (0, 1): # 0 = reboot required, 1 = OK
|
|
142
|
+
raise RuntimeError(f"EnableStatic (IPv4) failed, code {rc}")
|
|
143
|
+
|
|
144
|
+
# -------------------- Default Gateway via SetGateways -------------------
|
|
145
|
+
if gateways:
|
|
146
|
+
gateway_metrics = [1] * len(gateways)
|
|
147
|
+
in_params = network_config.Methods_("SetGateways").InParameters.SpawnInstance_()
|
|
148
|
+
in_params.DefaultIPGateway = gateways
|
|
149
|
+
in_params.GatewayCostMetric = [int(m) for m in gateway_metrics]
|
|
150
|
+
|
|
151
|
+
rc = network_config.ExecMethod_("SetGateways", in_params) \
|
|
152
|
+
.Properties_('ReturnValue').Value
|
|
153
|
+
if rc not in (0, 1):
|
|
154
|
+
raise RuntimeError(f"SetGateways failed, code {rc}")
|
|
155
|
+
|
|
156
|
+
# -------------------- DNS via SetDNSServerSearchOrder ------------------
|
|
157
|
+
if dns_gateways:
|
|
158
|
+
in_params = network_config.Methods_("SetDNSServerSearchOrder").InParameters.SpawnInstance_()
|
|
159
|
+
in_params.DNSServerSearchOrder = dns_gateways
|
|
160
|
+
|
|
161
|
+
rc = network_config.ExecMethod_("SetDNSServerSearchOrder", in_params).Properties_('ReturnValue').Value
|
|
162
|
+
if rc not in (0, 1):
|
|
163
|
+
raise RuntimeError(f"SetDNSServerSearchOrder failed, code {rc}")
|
|
164
|
+
|
|
165
|
+
# -------------------- Wait for the adapter to become available -----------
|
|
166
|
+
if availability_wait_seconds > 0:
|
|
167
|
+
count = 0
|
|
168
|
+
while count < 15:
|
|
169
|
+
current_default_ipv4: str = socket.gethostbyname(socket.gethostname())
|
|
170
|
+
if current_default_ipv4 == initial_default_ipv4:
|
|
171
|
+
# print(f"[+] Adapter is available: {current_default_ipv4}")
|
|
172
|
+
break
|
|
173
|
+
else:
|
|
174
|
+
# print(f"[!] Adapter is not available yet: [{current_default_ipv4}]")
|
|
175
|
+
count += 1
|
|
176
|
+
|
|
177
|
+
time.sleep(1)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def set_dynamic_ip(
|
|
181
|
+
nic_cfg,
|
|
182
|
+
reset_dns: bool = True,
|
|
183
|
+
reset_wins: bool = True
|
|
184
|
+
) -> None:
|
|
185
|
+
"""
|
|
186
|
+
Switch the adapter represented by *nic_cfg* (a Win32_NetworkAdapterConfiguration
|
|
187
|
+
COM object) to DHCP.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
nic_cfg : CDispatch
|
|
192
|
+
The adapter’s Win32_NetworkAdapterConfiguration instance (IPEnabled = TRUE).
|
|
193
|
+
reset_dns : bool, default True
|
|
194
|
+
Also clear any static DNS servers (calls SetDNSServerSearchOrder(None)).
|
|
195
|
+
reset_wins : bool, default True
|
|
196
|
+
Also clear any static WINS servers (calls SetWINSServer(None, None)).
|
|
197
|
+
|
|
198
|
+
Raises
|
|
199
|
+
------
|
|
200
|
+
RuntimeError
|
|
201
|
+
If any WMI call returns a status other than 0 (“Success”) or 1 (“Restart required”).
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
# 1) Turn on DHCP for IPv4
|
|
205
|
+
wmi_helpers.call_method(nic_cfg, 'EnableDHCP')
|
|
206
|
+
|
|
207
|
+
# 2) Clear static gateways (otherwise Windows keeps using them)
|
|
208
|
+
wmi_helpers.call_method(nic_cfg, 'SetGateways', ([], [])) # empty SAFEARRAY → remove gateways
|
|
209
|
+
|
|
210
|
+
# 3) Optional: reset DNS
|
|
211
|
+
if reset_dns:
|
|
212
|
+
wmi_helpers.call_method(nic_cfg, 'SetDNSServerSearchOrder', None) # None = DHCP-provided DNS
|
|
213
|
+
|
|
214
|
+
# 4) Optional: reset WINS
|
|
215
|
+
if reset_wins:
|
|
216
|
+
wmi_helpers.call_method(nic_cfg, 'SetWINSServer', ("", ""))
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def get_info_from_network_config(
|
|
220
|
+
network_config: CDispatch
|
|
221
|
+
) -> dict:
|
|
222
|
+
"""
|
|
223
|
+
Collect information about adapter that currently carries the default route.
|
|
224
|
+
|
|
225
|
+
:param network_config: CDispatch, Win32_NetworkAdapterConfiguration object.
|
|
226
|
+
:return: dict of the default adapter.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
def _split_ips(config):
|
|
230
|
+
"""Split IPAddress[] into separate v4 / v6 lists."""
|
|
231
|
+
current_ipv4s: list[str] = []
|
|
232
|
+
current_ipv4_masks: list[str] = []
|
|
233
|
+
current_ipv6s: list[str] = []
|
|
234
|
+
current_ipv6_prefixes: list[int] = []
|
|
235
|
+
for address_index, ip_address in enumerate(config.IPAddress):
|
|
236
|
+
if ip_addresses.is_ip_address(ip_address, 'ipv4'):
|
|
237
|
+
current_ipv4s.append(ip_address)
|
|
238
|
+
current_ipv4_masks.append(config.IPSubnet[address_index])
|
|
239
|
+
elif ip_addresses.is_ip_address(ip_address, 'ipv6'):
|
|
240
|
+
current_ipv6s.append(ip_address)
|
|
241
|
+
current_ipv6_prefixes.append(int(config.IPSubnet[address_index]))
|
|
242
|
+
|
|
243
|
+
return current_ipv4s, current_ipv6s, current_ipv4_masks, current_ipv6_prefixes
|
|
244
|
+
|
|
245
|
+
ipv4s, ipv6s, ipv4subnets, ipv6prefixes = _split_ips(network_config)
|
|
246
|
+
adapter = {
|
|
247
|
+
"caption": network_config.Caption,
|
|
248
|
+
"description": network_config.Description,
|
|
249
|
+
"interface_index": network_config.InterfaceIndex,
|
|
250
|
+
"is_dynamic": bool(network_config.DHCPEnabled),
|
|
251
|
+
"ipv4s": ipv4s,
|
|
252
|
+
"ipv6s": ipv6s,
|
|
253
|
+
"ipv4_subnet_masks": ipv4subnets,
|
|
254
|
+
"ipv6_prefixes": ipv6prefixes,
|
|
255
|
+
"default_gateways": list(network_config.DefaultIPGateway or []),
|
|
256
|
+
"dns_gateways": list(network_config.DNSServerSearchOrder or []),
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return adapter
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from logging import Logger
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from win32com.client import CDispatch
|
|
5
|
+
|
|
6
|
+
from . import wmi_helpers, win32_networkadapterconfiguration
|
|
7
|
+
from ...psutilw import psutil_networks
|
|
8
|
+
from ....print_api import print_api
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def list_network_adapters(wmi_instance: CDispatch = None) -> list[CDispatch]:
|
|
12
|
+
"""
|
|
13
|
+
List all network adapters on the system, from the Win32_NetworkAdapter class.
|
|
14
|
+
|
|
15
|
+
:param wmi_instance: WMI instance. You can get it from:
|
|
16
|
+
wmi_helpers.get_wmi_instance()
|
|
17
|
+
:return: list of Win32_NetworkAdapter objects.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
if not wmi_instance:
|
|
21
|
+
wmi_instance, _ = wmi_helpers.get_wmi_instance()
|
|
22
|
+
|
|
23
|
+
# Query all network adapters
|
|
24
|
+
adapters: list[CDispatch] = list(wmi_instance.ExecQuery("SELECT * FROM Win32_NetworkAdapter"))
|
|
25
|
+
|
|
26
|
+
# Print adapter descriptions
|
|
27
|
+
# for adapter in adapters:
|
|
28
|
+
# print(f"Description: {adapter.Description}, IPEnabled: {adapter.IPEnabled}")
|
|
29
|
+
return adapters
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_network_adapter_by_device_name(
|
|
33
|
+
device_name: str,
|
|
34
|
+
wmi_instance: CDispatch = None
|
|
35
|
+
) -> Union[CDispatch, None]:
|
|
36
|
+
"""
|
|
37
|
+
Get a network adapter by its name.
|
|
38
|
+
|
|
39
|
+
:param device_name: string, adapter name as shown in the network settings.
|
|
40
|
+
:param wmi_instance: WMI instance. You can get it from:
|
|
41
|
+
wmi_helpers.get_wmi_instance()
|
|
42
|
+
:return: Win32_NetworkAdapter object.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
if not wmi_instance:
|
|
46
|
+
wmi_instance, _ = wmi_helpers.get_wmi_instance()
|
|
47
|
+
|
|
48
|
+
query: str = (
|
|
49
|
+
"SELECT * FROM Win32_NetworkAdapter "
|
|
50
|
+
f"WHERE Name LIKE '{device_name}'")
|
|
51
|
+
adapters: list[CDispatch] = list(wmi_instance.ExecQuery(query))
|
|
52
|
+
if not adapters:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
return adapters[0]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_default_network_adapter(wmi_instance: CDispatch = None):
|
|
59
|
+
"""
|
|
60
|
+
Get the default network adapter.
|
|
61
|
+
|
|
62
|
+
:param wmi_instance: WMI instance. You can get it from:
|
|
63
|
+
wmi_helpers.get_wmi_instance()
|
|
64
|
+
:return:
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
if not wmi_instance:
|
|
68
|
+
wmi_instance, _ = wmi_helpers.get_wmi_instance()
|
|
69
|
+
|
|
70
|
+
default_connection_name_dict: dict = psutil_networks.get_default_connection_name()
|
|
71
|
+
# Get the first key from the dictionary.
|
|
72
|
+
default_connection_name: str = list(default_connection_name_dict.keys())[0]
|
|
73
|
+
adapters: list[CDispatch] = list_network_adapters(wmi_instance)
|
|
74
|
+
|
|
75
|
+
for adapter in adapters:
|
|
76
|
+
if default_connection_name == adapter.NetConnectionID:
|
|
77
|
+
return adapter
|
|
78
|
+
|
|
79
|
+
raise wmi_helpers.WMINetworkAdapterNotFoundError("Default network adapter not found.")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def set_dns_server(
|
|
83
|
+
dns_servers: Union[list[str], None],
|
|
84
|
+
interface_name: str = None,
|
|
85
|
+
mac_address: str = None,
|
|
86
|
+
verbose: bool = False,
|
|
87
|
+
logger: Logger = None
|
|
88
|
+
):
|
|
89
|
+
"""
|
|
90
|
+
Set the DNS servers for a network adapter.
|
|
91
|
+
:param dns_servers: list of strings, DNS server IPv4 addresses.
|
|
92
|
+
None, if you want to remove the DNS servers and make the interface to obtain them automatically from DHCP.
|
|
93
|
+
list[str], if you want to set the DNS servers manually to the list of strings.
|
|
94
|
+
:param interface_name: string, network interface name as shown in the network settings.
|
|
95
|
+
:param mac_address: string, MAC address of the adapter. Format: '00:00:00:00:00:00'.
|
|
96
|
+
|
|
97
|
+
:param verbose: bool, if True, print verbose output.
|
|
98
|
+
:param logger: Logger object, if provided, will log the output instead of printing.
|
|
99
|
+
:return:
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
adapter_config, current_adapter = win32_networkadapterconfiguration.get_adapter_network_configuration(
|
|
103
|
+
interface_name=interface_name, mac_address=mac_address)
|
|
104
|
+
|
|
105
|
+
if verbose:
|
|
106
|
+
message = (
|
|
107
|
+
f"Adapter [{current_adapter.Description}], Connection name [{current_adapter.NetConnectionID}]\n"
|
|
108
|
+
f"Setting DNS servers to {dns_servers}")
|
|
109
|
+
print_api(message, color='blue', logger=logger)
|
|
110
|
+
|
|
111
|
+
# Set DNS servers
|
|
112
|
+
wmi_helpers.call_method(adapter_config, 'SetDNSServerSearchOrder', dns_servers)
|