atomicshop 2.11.47__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/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- 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/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- 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/appointment_management.py +5 -3
- 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/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- 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 -74
- 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 +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- 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 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- 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/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- 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 +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- 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/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- 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 +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- 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 +500 -173
- 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 +14 -9
- 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 +157 -0
- 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.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- 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/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/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- 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/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- 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/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /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/mains → 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/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import win32evtlog
|
|
2
|
+
import xml.etree.ElementTree as Et
|
|
3
|
+
import time
|
|
4
|
+
import threading
|
|
5
|
+
import queue
|
|
6
|
+
from typing import Union
|
|
7
|
+
import binascii
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EventLogSubscriber:
|
|
11
|
+
"""
|
|
12
|
+
Class for subscribing to Windows Event Log events.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
from atomicshop.wrappers.pywin32w.win_event_log import subscribe
|
|
16
|
+
|
|
17
|
+
event_log_subscriber = subscribe.EventLogSubscriber('Security', 4688)
|
|
18
|
+
event_log_subscriber.start()
|
|
19
|
+
|
|
20
|
+
while True:
|
|
21
|
+
event = event_log_subscriber.emit()
|
|
22
|
+
print(event)
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, log_channel: str, event_id: int = None, provider: str = None):
|
|
25
|
+
"""
|
|
26
|
+
:param log_channel: The name of the event log channel to subscribe to. Examples:
|
|
27
|
+
Security, System, Application, etc.
|
|
28
|
+
:param event_id: The ID of the event to subscribe to.
|
|
29
|
+
Example: 4688 for process creation events in "Security" channel.
|
|
30
|
+
You can only subscribe by event ID or provider, not both.
|
|
31
|
+
:param provider: The name of the provider to subscribe to.
|
|
32
|
+
You can only subscribe by event ID or provider, not both.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
if event_id is None and provider is None:
|
|
36
|
+
raise ValueError("You must specify either an event ID or provider name to subscribe to.")
|
|
37
|
+
elif event_id and provider:
|
|
38
|
+
raise ValueError("You can only subscribe by event ID or provider, not both.")
|
|
39
|
+
|
|
40
|
+
self.log_channel: str = log_channel
|
|
41
|
+
self.provider: str = provider
|
|
42
|
+
|
|
43
|
+
if event_id:
|
|
44
|
+
self.event_id: str = str(event_id)
|
|
45
|
+
else:
|
|
46
|
+
self.event_id = event_id
|
|
47
|
+
|
|
48
|
+
self._event_queue = queue.Queue()
|
|
49
|
+
self._subscription_thread = None
|
|
50
|
+
|
|
51
|
+
def start(self):
|
|
52
|
+
"""Start the subscription process."""
|
|
53
|
+
self._subscription_thread = threading.Thread(
|
|
54
|
+
target=start_subscription, args=(self.log_channel, self._event_queue, self.event_id, self.provider)
|
|
55
|
+
)
|
|
56
|
+
self._subscription_thread.daemon = True
|
|
57
|
+
self._subscription_thread.start()
|
|
58
|
+
|
|
59
|
+
def stop(self):
|
|
60
|
+
"""Stop the subscription process."""
|
|
61
|
+
if self._subscription_thread:
|
|
62
|
+
self._subscription_thread.join()
|
|
63
|
+
self._subscription_thread = None
|
|
64
|
+
|
|
65
|
+
def emit(self, timeout: float = None) -> Union[dict, None]:
|
|
66
|
+
"""
|
|
67
|
+
Get the next event from the event queue.
|
|
68
|
+
|
|
69
|
+
:param timeout: The maximum time (in seconds) to wait for an event.
|
|
70
|
+
If None, the function will block until an event is available.
|
|
71
|
+
:return: A dictionary containing the event data, or None if no event is available.
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
return self._event_queue.get(timeout=timeout)
|
|
75
|
+
except queue.Empty:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _parse_event_xml(event_xml):
|
|
80
|
+
root = Et.fromstring(event_xml)
|
|
81
|
+
data = {}
|
|
82
|
+
|
|
83
|
+
# Helper function to strip namespace
|
|
84
|
+
def strip_namespace(tag):
|
|
85
|
+
return tag.split('}')[-1] # Remove namespace
|
|
86
|
+
|
|
87
|
+
# Iterate over all elements
|
|
88
|
+
for elem in root.iter():
|
|
89
|
+
# Extract elements with text content
|
|
90
|
+
if elem.text and elem.text.strip():
|
|
91
|
+
tag = elem.tag.split('}')[-1] # Remove namespace
|
|
92
|
+
data[tag] = elem.text.strip()
|
|
93
|
+
|
|
94
|
+
# Extract elements with attributes
|
|
95
|
+
for attr_name, attr_value in elem.attrib.items():
|
|
96
|
+
tag = elem.tag.split('}')[-1] # Remove namespace
|
|
97
|
+
data[f"{tag}_{attr_name}"] = attr_value
|
|
98
|
+
|
|
99
|
+
# Handle Binary data
|
|
100
|
+
if elem.tag.split('}')[-1] == 'Binary':
|
|
101
|
+
try:
|
|
102
|
+
data['BinaryReadable'] = binascii.unhexlify(elem.text.strip())
|
|
103
|
+
except (TypeError, binascii.Error) as e:
|
|
104
|
+
print(f"Error decoding binary data: {e}")
|
|
105
|
+
data['BinaryReadable'] = elem.text.strip()
|
|
106
|
+
|
|
107
|
+
# Extract system-specific data
|
|
108
|
+
system_data = root.find(".//{http://schemas.microsoft.com/win/2004/08/events/event}System")
|
|
109
|
+
if system_data is not None:
|
|
110
|
+
for system_elem in system_data:
|
|
111
|
+
tag = strip_namespace(system_elem.tag)
|
|
112
|
+
if system_elem.attrib:
|
|
113
|
+
for attr_name, attr_value in system_elem.attrib.items():
|
|
114
|
+
data[f"{tag}_{attr_name}"] = attr_value
|
|
115
|
+
if system_elem.text and system_elem.text.strip():
|
|
116
|
+
data[tag] = system_elem.text.strip()
|
|
117
|
+
|
|
118
|
+
# Extract event-specific data
|
|
119
|
+
event_data = root.find(".//{http://schemas.microsoft.com/win/2004/08/events/event}EventData")
|
|
120
|
+
if event_data is not None:
|
|
121
|
+
for data_elem in event_data:
|
|
122
|
+
if strip_namespace(data_elem.tag) == 'Data' and 'Name' in data_elem.attrib:
|
|
123
|
+
data[data_elem.attrib['Name']] = data_elem.text.strip()
|
|
124
|
+
|
|
125
|
+
# Extract user data if available
|
|
126
|
+
user_data = root.find(".//{http://schemas.microsoft.com/win/2004/08/events/event}UserData")
|
|
127
|
+
if user_data is not None:
|
|
128
|
+
for user_elem in user_data:
|
|
129
|
+
tag = strip_namespace(user_elem.tag)
|
|
130
|
+
if user_elem.attrib:
|
|
131
|
+
for attr_name, attr_value in user_elem.attrib.items():
|
|
132
|
+
data[f"{tag}_{attr_name}"] = attr_value
|
|
133
|
+
if user_elem.text and user_elem.text.strip():
|
|
134
|
+
data[tag] = user_elem.text.strip()
|
|
135
|
+
|
|
136
|
+
# Extract rendering info (additional details like the message)
|
|
137
|
+
rendering_info = root.find(".//{http://schemas.microsoft.com/win/2004/08/events/event}RenderingInfo")
|
|
138
|
+
if rendering_info is not None:
|
|
139
|
+
for info_elem in rendering_info:
|
|
140
|
+
tag = strip_namespace(info_elem.tag)
|
|
141
|
+
if info_elem.text and info_elem.text.strip():
|
|
142
|
+
data[f"RenderingInfo_{tag}"] = info_elem.text.strip()
|
|
143
|
+
|
|
144
|
+
return data
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _handle_event(event, event_queue):
|
|
148
|
+
# Render event as XML
|
|
149
|
+
event_xml = win32evtlog.EvtRender(event, win32evtlog.EvtRenderEventXml)
|
|
150
|
+
data = None
|
|
151
|
+
try:
|
|
152
|
+
data = _parse_event_xml(event_xml)
|
|
153
|
+
except Et.ParseError as e:
|
|
154
|
+
print(f"Error parsing event XML: {e}")
|
|
155
|
+
except Exception as e:
|
|
156
|
+
print(f"Error getting rendered message: {e}")
|
|
157
|
+
|
|
158
|
+
event_queue.put(data)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _event_callback(action, context, event):
|
|
162
|
+
event_queue = context['event_queue']
|
|
163
|
+
if action == win32evtlog.EvtSubscribeActionDeliver:
|
|
164
|
+
_handle_event(event, event_queue)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def start_subscription(
|
|
168
|
+
log_channel: str,
|
|
169
|
+
event_queue,
|
|
170
|
+
event_id: str = None,
|
|
171
|
+
provider: str = None
|
|
172
|
+
):
|
|
173
|
+
"""
|
|
174
|
+
Start listening for events in the specified log channel with the given event ID.
|
|
175
|
+
|
|
176
|
+
:param log_channel: The name of the event log channel to subscribe to. Examples:
|
|
177
|
+
Security, System, Application, etc.
|
|
178
|
+
:param event_queue: A queue to store the received events
|
|
179
|
+
:param event_id: The ID of the event to subscribe to.
|
|
180
|
+
Example: 4688 for process creation events in "Security" channel.
|
|
181
|
+
You can only subscribe by event ID or provider, not both.
|
|
182
|
+
:param provider: The name of the provider to subscribe to.
|
|
183
|
+
You can only subscribe by event ID or provider, not both.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
if event_id is None and provider is None:
|
|
187
|
+
raise ValueError("You must specify either an event ID or provider name to subscribe to.")
|
|
188
|
+
elif event_id and provider:
|
|
189
|
+
raise ValueError("You can only subscribe by event ID or provider, not both.")
|
|
190
|
+
|
|
191
|
+
# This selects the System node within each event.
|
|
192
|
+
# The System node contains metadata about the event, such as the event ID, provider name, timestamp, and more.
|
|
193
|
+
xpath_query = None
|
|
194
|
+
if provider:
|
|
195
|
+
xpath_query = f"*[System/Provider[@Name='{provider}']]"
|
|
196
|
+
elif event_id:
|
|
197
|
+
xpath_query = f"*[System/EventID={event_id}]"
|
|
198
|
+
|
|
199
|
+
subscription = win32evtlog.EvtSubscribe(
|
|
200
|
+
log_channel,
|
|
201
|
+
win32evtlog.EvtSubscribeToFutureEvents,
|
|
202
|
+
SignalEvent=None,
|
|
203
|
+
Query=xpath_query,
|
|
204
|
+
Callback=_event_callback,
|
|
205
|
+
Context={'event_queue': event_queue}
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
while True:
|
|
210
|
+
time.sleep(1)
|
|
211
|
+
except KeyboardInterrupt:
|
|
212
|
+
print("Stopped listening for events.")
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from .. import subscribe
|
|
2
|
+
from .... import win_auditw
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
LOG_CHANNEL: str = 'Security'
|
|
6
|
+
EVENT_ID: int = 4688
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
|
|
10
|
+
"""
|
|
11
|
+
Class for subscribing to Windows Event Log events related to process creation.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
from atomicshop.wrappers.pywin32w.win_event_log.subscribes import process_create
|
|
15
|
+
|
|
16
|
+
process_create_subscriber = process_create.ProcessCreateSubscriber()
|
|
17
|
+
process_create_subscriber.start()
|
|
18
|
+
|
|
19
|
+
while True:
|
|
20
|
+
event = process_create_subscriber.emit()
|
|
21
|
+
print(event)
|
|
22
|
+
"""
|
|
23
|
+
def __init__(self):
|
|
24
|
+
super().__init__(log_channel=LOG_CHANNEL, event_id=EVENT_ID)
|
|
25
|
+
|
|
26
|
+
def start(self):
|
|
27
|
+
"""Start the subscription process."""
|
|
28
|
+
win_auditw.enable_audit_process_creation()
|
|
29
|
+
win_auditw.enable_command_line_auditing()
|
|
30
|
+
|
|
31
|
+
super().start()
|
|
32
|
+
|
|
33
|
+
def stop(self):
|
|
34
|
+
"""Stop the subscription process."""
|
|
35
|
+
super().stop()
|
|
36
|
+
|
|
37
|
+
def emit(self, timeout: float = None) -> dict:
|
|
38
|
+
"""
|
|
39
|
+
Get the next event from the event queue.
|
|
40
|
+
|
|
41
|
+
:param timeout: The maximum time (in seconds) to wait for an event.
|
|
42
|
+
If None, the function will block until an event is available.
|
|
43
|
+
:return: A dictionary containing the event data.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
data = super().emit(timeout=timeout)
|
|
47
|
+
|
|
48
|
+
data['NewProcessIdInt'] = int(data['NewProcessId'], 16)
|
|
49
|
+
data['ParentProcessIdInt'] = int(data['ProcessId'], 16)
|
|
50
|
+
|
|
51
|
+
# if user_sid != "Unknown":
|
|
52
|
+
# try:
|
|
53
|
+
# user_name, domain, type = win32security.LookupAccountSid(None, user_sid)
|
|
54
|
+
# except Exception as e:
|
|
55
|
+
# print(f"Error looking up account SID: {e}")
|
|
56
|
+
|
|
57
|
+
return data
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from .. import subscribe
|
|
2
|
+
from .... import win_auditw
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
LOG_CHANNEL: str = 'Security'
|
|
6
|
+
EVENT_ID: int = 4689
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProcessTerminateSubscriber(subscribe.EventLogSubscriber):
|
|
10
|
+
"""
|
|
11
|
+
Class for subscribing to Windows Event Log events related to process termination.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
from atomicshop.wrappers.pywin32w.win_event_log.subscribes import process_terminate
|
|
15
|
+
|
|
16
|
+
process_terminate_subscriber = process_terminate.ProcessTerminateSubscriber()
|
|
17
|
+
process_terminate_subscriber.start()
|
|
18
|
+
|
|
19
|
+
while True:
|
|
20
|
+
event = process_terminate_subscriber.emit()
|
|
21
|
+
print(event)
|
|
22
|
+
"""
|
|
23
|
+
def __init__(self):
|
|
24
|
+
super().__init__(log_channel=LOG_CHANNEL, event_id=EVENT_ID)
|
|
25
|
+
|
|
26
|
+
def start(self):
|
|
27
|
+
"""Start the subscription process."""
|
|
28
|
+
win_auditw.enable_audit_process_termination()
|
|
29
|
+
|
|
30
|
+
super().start()
|
|
31
|
+
|
|
32
|
+
def stop(self):
|
|
33
|
+
"""Stop the subscription process."""
|
|
34
|
+
super().stop()
|
|
35
|
+
|
|
36
|
+
def emit(self, timeout: float = None) -> dict:
|
|
37
|
+
"""
|
|
38
|
+
Get the next event from the event queue.
|
|
39
|
+
|
|
40
|
+
:param timeout: The maximum time (in seconds) to wait for an event.
|
|
41
|
+
If None, the function will block until an event is available.
|
|
42
|
+
:return: A dictionary containing the event data.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
data = super().emit(timeout=timeout)
|
|
46
|
+
|
|
47
|
+
data['ProcessIdInt'] = int(data['ProcessId'], 16)
|
|
48
|
+
|
|
49
|
+
return data
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import winreg
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from .. import subscribe
|
|
5
|
+
from .....print_api import print_api
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
SCHANNEL_LOGGING_REG_PATH: str = r'SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL'
|
|
9
|
+
SCHANNEL_EVENT_LOGGING_KEY: str = 'EventLogging'
|
|
10
|
+
LOG_CHANNEL: str = 'System'
|
|
11
|
+
PROVIDER: str = 'Schannel'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SchannelLoggingSubscriber(subscribe.EventLogSubscriber):
|
|
15
|
+
"""
|
|
16
|
+
Class for subscribing to Windows Event Log events related to process creation.
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
from atomicshop.wrappers.pywin32w.win_event_log.subscribes import schannel_logging
|
|
20
|
+
|
|
21
|
+
process_create_subscriber = schannel_logging.SchannelLoggingSubscriber()
|
|
22
|
+
process_create_subscriber.start()
|
|
23
|
+
|
|
24
|
+
while True:
|
|
25
|
+
event = process_create_subscriber.emit()
|
|
26
|
+
print(event)
|
|
27
|
+
"""
|
|
28
|
+
def __init__(self):
|
|
29
|
+
super().__init__(log_channel=LOG_CHANNEL, provider=PROVIDER)
|
|
30
|
+
|
|
31
|
+
def start(self):
|
|
32
|
+
"""Start the subscription process."""
|
|
33
|
+
enable_schannel_logging()
|
|
34
|
+
|
|
35
|
+
super().start()
|
|
36
|
+
|
|
37
|
+
def stop(self):
|
|
38
|
+
"""Stop the subscription process."""
|
|
39
|
+
super().stop()
|
|
40
|
+
|
|
41
|
+
def emit(self, timeout: float = None) -> dict:
|
|
42
|
+
"""
|
|
43
|
+
Get the next event from the event queue.
|
|
44
|
+
|
|
45
|
+
:param timeout: The maximum time (in seconds) to wait for an event.
|
|
46
|
+
If None, the function will block until an event is available.
|
|
47
|
+
:return: A dictionary containing the event data.
|
|
48
|
+
"""
|
|
49
|
+
return super().emit(timeout=timeout)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def enable_schannel_logging(logging_value: int = 1, print_kwargs: dict = None):
|
|
53
|
+
"""
|
|
54
|
+
Value Description
|
|
55
|
+
0x0000 Do not log
|
|
56
|
+
0x0001 Log error messages
|
|
57
|
+
0x0002 Log warnings
|
|
58
|
+
0x0003 Log warnings and error messages
|
|
59
|
+
0x0004 Log informational and success events
|
|
60
|
+
0x0005 Log informational, success events and error messages
|
|
61
|
+
0x0006 Log informational, success events and warnings
|
|
62
|
+
0x0007 Log informational, success events, warnings, and error messages (all log levels)
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
if is_schannel_logging_enabled(logging_value):
|
|
66
|
+
print_api(
|
|
67
|
+
"Schannel event logging is already enabled.", color='yellow',
|
|
68
|
+
**(print_kwargs or {}))
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, SCHANNEL_LOGGING_REG_PATH, 0, winreg.KEY_ALL_ACCESS) as key:
|
|
73
|
+
winreg.SetValueEx(key, SCHANNEL_EVENT_LOGGING_KEY, 0, winreg.REG_DWORD, logging_value)
|
|
74
|
+
|
|
75
|
+
print_api(
|
|
76
|
+
"Successfully enabled Schannel logging.",
|
|
77
|
+
color='green', **(print_kwargs or {}))
|
|
78
|
+
print_api(
|
|
79
|
+
"Please restart the computer for the changes to take effect.",
|
|
80
|
+
color='yellow', **(print_kwargs or {}))
|
|
81
|
+
sys.exit()
|
|
82
|
+
except WindowsError as e:
|
|
83
|
+
print_api(
|
|
84
|
+
f"Failed to enable Schannel event logging: {e}", error_type=True,
|
|
85
|
+
color='red', **(print_kwargs or {}))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def is_schannel_logging_enabled(logging_value: int) -> bool:
|
|
89
|
+
try:
|
|
90
|
+
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, SCHANNEL_LOGGING_REG_PATH, 0, winreg.KEY_READ) as key:
|
|
91
|
+
value, regtype = winreg.QueryValueEx(key, SCHANNEL_EVENT_LOGGING_KEY)
|
|
92
|
+
return value == logging_value
|
|
93
|
+
except FileNotFoundError:
|
|
94
|
+
return False
|
|
95
|
+
except WindowsError as e:
|
|
96
|
+
print(f"Failed to read the Schannel event logging setting: {e}")
|
|
97
|
+
return False
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from win32com.client import Dispatch
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def create_shortcut(file_path_to_link: str, shortcut_file_path: str):
|
|
7
|
+
"""
|
|
8
|
+
Create a shortcut in the startup folder to the specified file.
|
|
9
|
+
|
|
10
|
+
:param file_path_to_link: The path to the file you want to create a shortcut to.
|
|
11
|
+
:param shortcut_file_path: The name of the shortcut file. Should be with the ".lnk" extension.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
shell = Dispatch('WScript.Shell')
|
|
15
|
+
shortcut = shell.CreateShortCut(shortcut_file_path)
|
|
16
|
+
shortcut.Targetpath = file_path_to_link
|
|
17
|
+
shortcut.WorkingDirectory = str(Path(file_path_to_link).parent)
|
|
18
|
+
shortcut.Description = f"Shortcut for {Path(file_path_to_link).name}"
|
|
19
|
+
shortcut.save()
|
|
File without changes
|
|
@@ -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)
|