atomicshop 2.12.23__py3-none-any.whl → 2.12.25__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.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.12.23'
4
+ __version__ = '2.12.25'
@@ -0,0 +1,5 @@
1
+ from ..wrappers.ctyping.etw_winapi import etw_functions
2
+
3
+
4
+ def get_providers():
5
+ return etw_functions.get_all_providers()
@@ -0,0 +1,43 @@
1
+ from ..wrappers.ctyping.etw_winapi import etw_functions
2
+
3
+
4
+ def stop_and_delete(session_name) -> tuple[bool, int]:
5
+ """
6
+ Stop and delete ETW session.
7
+
8
+ :param session_name: The name of the session to stop and delete.
9
+ :return: A tuple containing a boolean indicating success and an integer status code.
10
+ True, 0: If the session was stopped and deleted successfully.
11
+ False, <status>: If the session could not be stopped and deleted and the status code, why it failed.
12
+ """
13
+
14
+ return etw_functions.stop_and_delete_etw_session(session_name)
15
+
16
+
17
+ def get_running_list() -> list[dict]:
18
+ """
19
+ List all running ETW sessions.
20
+
21
+ :return: A list of strings containing the names of all running ETW sessions.
22
+ """
23
+
24
+ return etw_functions.list_etw_sessions()
25
+
26
+
27
+ def is_session_running(session_name: str) -> bool:
28
+ """
29
+ Check if an ETW session is running.
30
+
31
+ :param session_name: The name of the session to check.
32
+ :return: A boolean indicating if the session is running.
33
+ """
34
+
35
+ # Get all running sessions.
36
+ running_sessions = get_running_list()
37
+
38
+ # Check if the session is in the list of running sessions.
39
+ for session in running_sessions:
40
+ if session['session_name'] == session_name:
41
+ return True
42
+
43
+ return False
@@ -1,9 +1,11 @@
1
1
  import queue
2
+ import sys
2
3
 
3
4
  # Import FireEye Event Tracing library.
4
5
  import etw
5
6
 
6
- from ..basics import atexits
7
+ from ..print_api import print_api
8
+ from . import sessions
7
9
 
8
10
 
9
11
  class EventTrace(etw.ETW):
@@ -13,8 +15,8 @@ class EventTrace(etw.ETW):
13
15
  event_callback=None,
14
16
  event_id_filters: list = None,
15
17
  session_name: str = None,
16
- stop_etw_tracing_on_exit: bool = False
17
- ):
18
+ close_existing_session_name: bool = True
19
+ ):
18
20
  """
19
21
  :param providers: List of tuples with provider name and provider GUID.
20
22
  tuple[0] = provider name
@@ -23,7 +25,9 @@ class EventTrace(etw.ETW):
23
25
  :param event_id_filters: List of event IDs that we want to filter. If not provided, all events will be returned.
24
26
  The default in the 'etw.ETW' method is 'None'.
25
27
  :param session_name: The name of the session to create. If not provided, a UUID will be generated.
26
- :param stop_etw_tracing_on_exit: If True, the ETW tracing will be stopped when the script exits.
28
+ :param close_existing_session_name: Boolean to close existing session names.
29
+ ------------------------------------------
30
+ You should stop the ETW tracing when you are done with it.
27
31
  'pywintrace' module starts a new session for ETW tracing, and it will not stop the session when the script
28
32
  exits or exception is raised.
29
33
  This can cause problems when you want to start the script again, and the session is already running.
@@ -32,11 +36,16 @@ class EventTrace(etw.ETW):
32
36
  If you give different session name for new session, the previous session will still continue to run,
33
37
  filling the buffer with events, until you will stop getting new events on all sessions or get an
34
38
  exception that the buffer is full (WinError 1450).
39
+
40
+ Example to stop the ETW tracing at the end of the script:
41
+ from atomicshop.basics import atexits
42
+
43
+
44
+ event_tracing = EventTrace(<Your parameters>)
45
+ atexits.run_callable_on_exit_and_signals(EventTrace.stop)
35
46
  """
36
47
  self.event_queue = queue.Queue()
37
- self.stop_etw_tracing_on_exit: bool = stop_etw_tracing_on_exit
38
-
39
- self._set_atexit_and_signals()
48
+ self.close_existing_session_name: bool = close_existing_session_name
40
49
 
41
50
  # If no callback function is provided, we will use the default one, which will put the event in the queue.
42
51
  if not event_callback:
@@ -56,11 +65,24 @@ class EventTrace(etw.ETW):
56
65
  )
57
66
 
58
67
  def start(self):
68
+ # Check if the session name already exists.
69
+ if sessions.is_session_running(self.session_name):
70
+ print_api(f'ETW Session already running: {self.session_name}', color='yellow')
71
+
72
+ # Close the existing session name.
73
+ if self.close_existing_session_name:
74
+ print_api(f'Closing existing session: {self.session_name}', color='blue')
75
+ sessions.stop_and_delete(self.session_name)
76
+ else:
77
+ print_api(f'Using existing session: {self.session_name}', color='yellow')
78
+
59
79
  try:
60
80
  super().start()
61
81
  except OSError as e:
62
- raise OSError(f"PyWinTrace Error: {e}\n"
63
- f"PyWinTrace crashed, didn't find solution to this, RESTART computer.")
82
+ message = f"PyWinTrace Error: {e}\n" \
83
+ f"PyWinTrace crashed, didn't find solution to this, RESTART computer."
84
+ print_api(message, error_type=True, logger_method='critical')
85
+ sys.exit(1)
64
86
 
65
87
  def stop(self):
66
88
  super().stop()
@@ -84,10 +106,6 @@ class EventTrace(etw.ETW):
84
106
 
85
107
  return self.event_queue.get()
86
108
 
87
- def _set_atexit_and_signals(self):
88
- if self.stop_etw_tracing_on_exit:
89
- atexits.run_callable_on_exit_and_signals(self.stop)
90
-
91
109
 
92
110
  def find_sessions_by_provider(provider_name: str):
93
111
  """
@@ -1,4 +1,4 @@
1
- from . import etw
1
+ from . import trace
2
2
  from .. import dns
3
3
  from ..wrappers.psutilw import psutilw
4
4
  from ..basics import dicts
@@ -7,7 +7,13 @@ from ..print_api import print_api
7
7
 
8
8
 
9
9
  class DnsTrace:
10
- def __init__(self, enable_process_poller: bool = False, attrs: list = None, session_name: str = None):
10
+ def __init__(
11
+ self,
12
+ enable_process_poller: bool = False,
13
+ attrs: list = None,
14
+ session_name: str = None,
15
+ close_existing_session_name: bool = True
16
+ ):
11
17
  """
12
18
  DnsTrace class use to trace DNS events from Windows Event Tracing for EventId 3008.
13
19
 
@@ -16,6 +22,14 @@ class DnsTrace:
16
22
  Then DNS events will be enriched with the process name and command line from the process poller.
17
23
  :param attrs: List of attributes to return. If None, all attributes will be returned.
18
24
  :param session_name: The name of the session to create. If not provided, a UUID will be generated.
25
+ :param close_existing_session_name: Boolean to close existing session names.
26
+ True: if ETW session with 'session_name' exists, you will be notified and the session will be closed.
27
+ Then the new session with this name will be created.
28
+ False: if ETW session with 'session_name' exists, you will be notified and the new session will not be
29
+ created. Instead, the existing session will be used. If there is a buffer from the previous session,
30
+ you will get the events from the buffer.
31
+
32
+ -------------------------------------------------
19
33
 
20
34
  Usage Example:
21
35
  from atomicshop.etw import dns_trace
@@ -32,11 +46,12 @@ class DnsTrace:
32
46
  self.enable_process_poller = enable_process_poller
33
47
  self.attrs = attrs
34
48
 
35
- self.event_trace = etw.EventTrace(
49
+ self.event_trace = trace.EventTrace(
36
50
  providers=[(dns.ETW_DNS_INFO['provider_name'], dns.ETW_DNS_INFO['provider_guid'])],
37
51
  # lambda x: self.event_queue.put(x),
38
52
  event_id_filters=[dns.ETW_DNS_INFO['event_id']],
39
- session_name=session_name
53
+ session_name=session_name,
54
+ close_existing_session_name=close_existing_session_name
40
55
  )
41
56
 
42
57
  if self.enable_process_poller:
@@ -1,7 +1,7 @@
1
1
  from pathlib import Path
2
2
  from typing import Union
3
3
 
4
- from ...etw.dns_trace import DnsTrace
4
+ from ...etw.trace_dns import DnsTrace
5
5
  from ...print_api import print_api
6
6
  from ...import diff_check
7
7
 
@@ -30,7 +30,7 @@ class DnsCheck:
30
30
  self.fetch_engine: DnsTrace = (
31
31
  DnsTrace(
32
32
  enable_process_poller=True, attrs=['name', 'cmdline', 'domain', 'query_type'],
33
- session_name=self.etw_session_name)
33
+ session_name=self.etw_session_name, close_existing_session_name=True)
34
34
  )
35
35
 
36
36
  if self.settings['alert_always'] and self.settings['alert_about_missing_entries_after_learning']:
atomicshop/on_exit.py ADDED
@@ -0,0 +1,175 @@
1
+ import atexit
2
+ import signal
3
+ import sys
4
+ import platform
5
+
6
+ import win32api
7
+ import win32con
8
+
9
+ from .print_api import print_api
10
+ from .wrappers.pywin32w import console
11
+
12
+
13
+ EXIT_HANDLER_INSTANCE = None
14
+
15
+
16
+ class ExitHandler:
17
+ """
18
+ This class is used to handle exit events: Closing the console, pressing 'CTRL+C', Killing the process.
19
+
20
+ Example:
21
+ from atomicshop import on_exit
22
+
23
+
24
+ def clean_up_function():
25
+ print("Cleaning up.")
26
+
27
+
28
+ on_exit.ExitHandler(clean_up_function).register_handlers()
29
+
30
+ # OR
31
+ on_exit.register_exit_handler(clean_up_function)
32
+ """
33
+ def __init__(
34
+ self,
35
+ cleanup_action: callable,
36
+ args: tuple = None,
37
+ kwargs: dict = None
38
+ ):
39
+ """
40
+ :param cleanup_action: The action to run when one of exit types is triggered.
41
+ :param args: The arguments to pass to the cleanup action.
42
+ :param kwargs: The keyword arguments to pass to the cleanup action.
43
+ """
44
+
45
+ if not callable(cleanup_action):
46
+ raise ValueError("The 'cleanup_action' must be a callable function.")
47
+
48
+ if args is None:
49
+ args = tuple()
50
+
51
+ if kwargs is None:
52
+ kwargs = dict()
53
+
54
+ self.cleanup_action: callable = cleanup_action
55
+ self.args: tuple = args
56
+ self.kwargs: dict = kwargs
57
+
58
+ self._called: bool = False
59
+ self._handler_hit: bool = False
60
+
61
+ def _run_cleanup(self):
62
+ if not self._called:
63
+ self._called = True
64
+ self.cleanup_action(*self.args, **self.kwargs)
65
+
66
+ def console_handler(self, event):
67
+ if event == win32con.CTRL_CLOSE_EVENT:
68
+
69
+ if not self._handler_hit:
70
+ self._handler_hit = True
71
+
72
+ print("Console close event.")
73
+ self._run_cleanup()
74
+
75
+ return True
76
+ return False
77
+
78
+ def atexit_handler(self):
79
+ if not self._handler_hit:
80
+ self._handler_hit = True
81
+
82
+ print("atexit_handler")
83
+ self._run_cleanup()
84
+
85
+ def signal_handler(self, signum, frame):
86
+ if not self._handler_hit:
87
+ self._handler_hit = True
88
+
89
+ print_api(f"Signal {signum}")
90
+ self._run_cleanup()
91
+ # Exit the process gracefully
92
+ raise SystemExit(0)
93
+
94
+ def register_handlers(self):
95
+ win32api.SetConsoleCtrlHandler(self.console_handler, True)
96
+ atexit.register(self.atexit_handler)
97
+ signal.signal(signal.SIGINT, self.signal_handler)
98
+ signal.signal(signal.SIGTERM, self.signal_handler)
99
+
100
+
101
+ def register_exit_handler(clean_up_function, *args, **kwargs):
102
+ """
103
+ This function will register the exit handler to handle exit events: Closing the console, pressing 'CTRL+C',
104
+ Killing the process.
105
+
106
+ :param clean_up_function: The action to run when one of exit types is triggered.
107
+ :param args: The arguments to pass to the cleanup action.
108
+ :param kwargs: The keyword arguments to pass to the cleanup action.
109
+ """
110
+ global EXIT_HANDLER_INSTANCE
111
+ EXIT_HANDLER_INSTANCE = ExitHandler(clean_up_function, args, kwargs)
112
+ EXIT_HANDLER_INSTANCE.register_handlers()
113
+
114
+
115
+ def restart_function(callable_function, *args, **kwargs):
116
+ """
117
+ This function will run the callable function with the given arguments and keyword arguments.
118
+ If the function raises an exception, the function will be restarted.
119
+
120
+ :param callable_function: The function to run.
121
+ :param args: The arguments to pass to the function.
122
+ :param kwargs: The keyword arguments to pass to the function.
123
+ :return: The return value of the function.
124
+ """
125
+ while True:
126
+ try:
127
+ return callable_function(*args, **kwargs)
128
+ except Exception as e:
129
+ print(f"ERROR: {e}")
130
+ continue
131
+
132
+
133
+ def _ref_run_callable_on_exit_and_signals(
134
+ callable_function,
135
+ print_kwargs: dict = None,
136
+ *args,
137
+ **kwargs
138
+ ):
139
+ """
140
+ THIS IS FOR REFERENCE ONLY.
141
+
142
+ This function will run the callable function with the given arguments and keyword arguments.
143
+ If the function raises an exception, the function will be restarted.
144
+
145
+ :param callable_function: The function to run.
146
+ :param print_kwargs: print_api kwargs.
147
+ :param args: The arguments to pass to the function.
148
+ :param kwargs: The keyword arguments to pass to the function.
149
+ :return: The return value of the function.
150
+ """
151
+ def signal_handler(signum, frame):
152
+ print_api(f"Signal {signum} received, exiting.", **(print_kwargs or {}))
153
+ callable_function(*args, **kwargs)
154
+ sys.exit(0)
155
+
156
+ def exit_handler():
157
+ print_api("Exiting.", **(print_kwargs or {}))
158
+ callable_function(*args, **kwargs)
159
+ sys.exit(0)
160
+
161
+ signals = [signal.SIGINT, signal.SIGTERM]
162
+ if platform.system() != 'Windows':
163
+ signals.append(signal.SIGQUIT)
164
+ signals.append(signal.SIGHUP)
165
+ for sig in signals:
166
+ signal.signal(sig, signal_handler)
167
+
168
+ # signal.signal(signal.SIGINT, signal_handler)
169
+ # signal.signal(signal.SIGTERM, signal_handler)
170
+ atexit.register(exit_handler)
171
+
172
+ # Register console handler for Windows.
173
+ if platform.system() == 'Windows':
174
+ console_handler = console.ConsoleHandler(exit_handler, args, kwargs)
175
+ console_handler.register_handler()
@@ -1,4 +1,4 @@
1
- from importlib import resources
1
+ from importlib.resources import files
2
2
  import ctypes
3
3
  from ctypes import wintypes
4
4
  from typing import Literal
@@ -8,20 +8,19 @@ from .basics import list_of_dicts
8
8
 
9
9
 
10
10
  PACKAGE_DLL_PATH = 'addons/process_list/compiled/Win10x64/process_list.dll'
11
+ FULL_DLL_PATH = str(files(__package__).joinpath(PACKAGE_DLL_PATH))
11
12
 
12
13
 
13
14
  class ProcessNameCmdline:
14
15
  def __init__(self, load_dll: bool = False):
15
16
  self.load_dll: bool = load_dll
17
+ self.dll_path = FULL_DLL_PATH
16
18
 
17
19
  self.dll = None
18
20
  self.callback_output = None
19
21
  self.CALLBACKFUNC = None
20
22
  self.CALLBACKFUNC_ref = None
21
23
 
22
- with resources.path(__package__, PACKAGE_DLL_PATH) as dll_path:
23
- self.dll_path = str(dll_path)
24
-
25
24
  if self.load_dll:
26
25
  self.load()
27
26
 
@@ -207,8 +207,7 @@ class ProcessPollerPool:
207
207
 
208
208
  def _start_process(self):
209
209
  self.running = True
210
- stopping_queue = multiprocessing.Queue()
211
- multiprocessing.Process(target=self._worker, args=(stopping_queue,)).start()
210
+ multiprocessing.Process(target=self._worker).start()
212
211
 
213
212
  thread = threading.Thread(target=self._thread_get_queue)
214
213
  thread.daemon = True
File without changes
@@ -0,0 +1,94 @@
1
+ import ctypes
2
+ from ctypes import wintypes
3
+ from ctypes.wintypes import ULONG
4
+
5
+
6
+ # Constants
7
+ EVENT_TRACE_CONTROL_STOP = 1
8
+ WNODE_FLAG_TRACED_GUID = 0x00020000
9
+
10
+ MAXIMUM_LOGGERS = 64
11
+
12
+
13
+ # Define GUID structure
14
+ class GUID(ctypes.Structure):
15
+ _fields_ = [
16
+ ("Data1", wintypes.DWORD),
17
+ ("Data2", wintypes.WORD),
18
+ ("Data3", wintypes.WORD),
19
+ ("Data4", wintypes.BYTE * 8)
20
+ ]
21
+
22
+
23
+ # Define WNODE_HEADER
24
+ class WNODE_HEADER(ctypes.Structure):
25
+ _fields_ = [
26
+ ("BufferSize", wintypes.ULONG),
27
+ ("ProviderId", wintypes.ULONG),
28
+ ("HistoricalContext", wintypes.LARGE_INTEGER),
29
+ ("TimeStamp", wintypes.LARGE_INTEGER),
30
+ ("Guid", GUID),
31
+ ("ClientContext", wintypes.ULONG),
32
+ ("Flags", wintypes.ULONG)
33
+ ]
34
+
35
+
36
+ # Define EVENT_TRACE_PROPERTIES structure
37
+ class EVENT_TRACE_PROPERTIES(ctypes.Structure):
38
+ _fields_ = [
39
+ ("Wnode", WNODE_HEADER),
40
+ ("BufferSize", wintypes.ULONG),
41
+ ("MinimumBuffers", wintypes.ULONG),
42
+ ("MaximumBuffers", wintypes.ULONG),
43
+ ("MaximumFileSize", wintypes.ULONG),
44
+ ("LogFileMode", wintypes.ULONG),
45
+ ("FlushTimer", wintypes.ULONG),
46
+ ("EnableFlags", wintypes.ULONG),
47
+ ("AgeLimit", wintypes.LONG),
48
+ ("NumberOfBuffers", wintypes.ULONG),
49
+ ("FreeBuffers", wintypes.ULONG),
50
+ ("EventsLost", wintypes.ULONG),
51
+ ("BuffersWritten", wintypes.ULONG),
52
+ ("LogBuffersLost", wintypes.ULONG),
53
+ ("RealTimeBuffersLost", wintypes.ULONG),
54
+ ("LoggerThreadId", wintypes.HANDLE),
55
+ ("LogFileNameOffset", wintypes.ULONG),
56
+ ("LoggerNameOffset", wintypes.ULONG),
57
+ # Allocate space for the names at the end of the structure
58
+ ("_LoggerName", wintypes.WCHAR * 1024),
59
+ ("_LogFileName", wintypes.WCHAR * 1024)
60
+ ]
61
+
62
+
63
+ class PROVIDER_ENUMERATION_INFO(ctypes.Structure):
64
+ _fields_ = [
65
+ ("NumberOfProviders", ULONG),
66
+ ("Reserved", ULONG),
67
+ ]
68
+
69
+
70
+ class PROVIDER_INFORMATION(ctypes.Structure):
71
+ _fields_ = [
72
+ ("ProviderId", ctypes.c_byte * 16),
73
+ ("SchemaSource", ULONG),
74
+ ("ProviderNameOffset", ULONG),
75
+ ]
76
+
77
+
78
+ # Load the necessary library
79
+ advapi32 = ctypes.WinDLL('advapi32')
80
+ tdh = ctypes.windll.tdh
81
+
82
+ # Define necessary TDH functions
83
+ tdh.TdhEnumerateProviders.argtypes = [ctypes.POINTER(PROVIDER_ENUMERATION_INFO), ctypes.POINTER(ULONG)]
84
+ tdh.TdhEnumerateProviders.restype = ULONG
85
+
86
+
87
+ # Define the function prototype
88
+ QueryAllTraces = advapi32.QueryAllTracesW
89
+ QueryAllTraces.argtypes = [
90
+ ctypes.POINTER(ctypes.POINTER(EVENT_TRACE_PROPERTIES)),
91
+ wintypes.ULONG,
92
+ ctypes.POINTER(wintypes.ULONG)
93
+ ]
94
+ QueryAllTraces.restype = wintypes.ULONG
@@ -0,0 +1,128 @@
1
+ import ctypes
2
+ from ctypes.wintypes import ULONG
3
+ import uuid
4
+
5
+ from . import const
6
+
7
+
8
+ # Function to stop and delete ETW session
9
+ def stop_and_delete_etw_session(session_name) -> tuple[bool, int]:
10
+ """
11
+ Stop and delete ETW session.
12
+
13
+ :param session_name: The name of the session to stop and delete.
14
+ :return: A tuple containing a boolean indicating success and an integer status code.
15
+ True, 0: If the session was stopped and deleted successfully.
16
+ False, <status>: If the session could not be stopped and deleted and the status code, why it failed.
17
+ """
18
+
19
+ session_name_unicode = ctypes.create_unicode_buffer(session_name)
20
+ properties_size = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES) + 1024 # Adjust buffer size if needed
21
+ properties = ctypes.create_string_buffer(properties_size)
22
+
23
+ trace_properties = ctypes.cast(properties, ctypes.POINTER(const.EVENT_TRACE_PROPERTIES)).contents
24
+ trace_properties.Wnode.BufferSize = properties_size
25
+ trace_properties.Wnode.Flags = const.WNODE_FLAG_TRACED_GUID
26
+ trace_properties.Wnode.Guid = const.GUID() # Ensure a GUID is provided if necessary
27
+ trace_properties.LoggerNameOffset = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
28
+
29
+ ctypes.memmove(ctypes.addressof(properties) + trace_properties.LoggerNameOffset,
30
+ session_name_unicode, ctypes.sizeof(session_name_unicode))
31
+
32
+ status = const.advapi32.ControlTraceW(
33
+ None, session_name_unicode, ctypes.byref(trace_properties), const.EVENT_TRACE_CONTROL_STOP)
34
+
35
+ if status != 0:
36
+ # print(f"Failed to stop and delete ETW session: {status}")
37
+ return False, status
38
+ else:
39
+ # print("ETW session stopped and deleted successfully.")
40
+ return True, status
41
+
42
+
43
+ def get_all_providers() -> list[tuple[any, uuid.UUID]]:
44
+ """
45
+ Get all ETW providers available on the system.
46
+
47
+ :return: A list of tuples containing the provider name and GUID.
48
+ """
49
+
50
+ providers_info_size = ULONG(0)
51
+ status = const.tdh.TdhEnumerateProviders(None, ctypes.byref(providers_info_size))
52
+
53
+ # Initial allocation
54
+ buffer = (ctypes.c_byte * providers_info_size.value)()
55
+ providers_info = ctypes.cast(buffer, ctypes.POINTER(const.PROVIDER_ENUMERATION_INFO))
56
+
57
+ # Loop to handle resizing
58
+ while True:
59
+ status = const.tdh.TdhEnumerateProviders(providers_info, ctypes.byref(providers_info_size))
60
+
61
+ if status == 0:
62
+ break
63
+ elif status == 0x8007007A: # ERROR_INSUFFICIENT_BUFFER
64
+ buffer = (ctypes.c_byte * providers_info_size.value)()
65
+ providers_info = ctypes.cast(buffer, ctypes.POINTER(const.PROVIDER_ENUMERATION_INFO))
66
+ else:
67
+ raise ctypes.WinError(status)
68
+
69
+ provider_count = providers_info.contents.NumberOfProviders
70
+ provider_array = ctypes.cast(
71
+ ctypes.addressof(providers_info.contents) + ctypes.sizeof(const.PROVIDER_ENUMERATION_INFO),
72
+ ctypes.POINTER(const.PROVIDER_INFORMATION * provider_count))
73
+
74
+ providers = []
75
+ for i in range(provider_count):
76
+ provider = provider_array.contents[i]
77
+ provider_name_offset = provider.ProviderNameOffset
78
+ provider_name_ptr = ctypes.cast(
79
+ ctypes.addressof(providers_info.contents) + provider_name_offset, ctypes.c_wchar_p)
80
+ provider_name = provider_name_ptr.value
81
+ provider_guid = uuid.UUID(bytes_le=bytes(provider.ProviderId))
82
+ providers.append((provider_name, provider_guid))
83
+
84
+ return providers
85
+
86
+
87
+ def list_etw_sessions() -> list[dict]:
88
+ """
89
+ List all running ETW sessions.
90
+
91
+ :return: A list of dictionaries containing the names of all running ETW sessions and their log files.
92
+ """
93
+ # Create an array of EVENT_TRACE_PROPERTIES pointers
94
+ PropertiesArrayType = ctypes.POINTER(const.EVENT_TRACE_PROPERTIES) * const.MAXIMUM_LOGGERS
95
+ properties_array = PropertiesArrayType()
96
+ for i in range(const.MAXIMUM_LOGGERS):
97
+ properties = const.EVENT_TRACE_PROPERTIES()
98
+ properties.Wnode.BufferSize = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
99
+ properties_array[i] = ctypes.pointer(properties)
100
+
101
+ # Define the number of loggers variable
102
+ logger_count = ULONG(const.MAXIMUM_LOGGERS)
103
+
104
+ # Call QueryAllTraces
105
+ status = const.QueryAllTraces(properties_array, const.MAXIMUM_LOGGERS, ctypes.byref(logger_count))
106
+ if status != 0:
107
+ raise Exception(f"QueryAllTraces failed, error code: {status}")
108
+
109
+ # Extract session names
110
+ session_list: list = []
111
+ for i in range(logger_count.value):
112
+ logger_name = None
113
+ logfile_path = None
114
+
115
+ properties = properties_array[i].contents
116
+ if properties.LoggerNameOffset != 0:
117
+ logger_name_address = ctypes.addressof(properties) + properties.LoggerNameOffset
118
+ logger_name = ctypes.wstring_at(logger_name_address)
119
+ if properties.LogFileNameOffset != 0:
120
+ logfile_name_address = ctypes.addressof(properties) + properties.LogFileNameOffset
121
+ logfile_path = ctypes.wstring_at(logfile_name_address)
122
+
123
+ session_list.append({
124
+ 'session_name': logger_name,
125
+ 'log_file': logfile_path
126
+ })
127
+
128
+ return session_list
@@ -0,0 +1,34 @@
1
+ import win32api
2
+ import win32con
3
+
4
+
5
+ class ConsoleHandler:
6
+ """
7
+ This class is used to handle console events.
8
+ Currently used to handle the 'CTRL_CLOSE_EVENT' event - Meaning what to do when the user closes the console by
9
+ clicking on X in the top right corner.
10
+ """
11
+ def __init__(
12
+ self,
13
+ cleanup_action: callable = None,
14
+ args: tuple = None,
15
+ kwargs: dict = None
16
+ ):
17
+ """
18
+ :param cleanup_action: The action to run when user closes the console.
19
+ :param args: The arguments to pass to the cleanup action.
20
+ :param kwargs: The keyword arguments to pass to the cleanup action.
21
+ """
22
+ self.cleanup_action = cleanup_action
23
+ self.args = args
24
+ self.kwargs = kwargs
25
+
26
+ def _console_handler(self, event):
27
+ if event == win32con.CTRL_CLOSE_EVENT:
28
+ if self.cleanup_action and callable(self.cleanup_action):
29
+ self.cleanup_action(*self.args, **self.kwargs)
30
+ return True
31
+ return False
32
+
33
+ def register_handler(self):
34
+ win32api.SetConsoleCtrlHandler(self._console_handler, True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.12.23
3
+ Version: 2.12.25
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=8aamirvtHe04uZCHVaHjJnr7Y7nVlrXMflv_x72OTIo,124
1
+ atomicshop/__init__.py,sha256=i0XAOpCKxA2g_BNalWhe-R5FvF3ui38CZEoR2TgPwIg,124
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -21,12 +21,13 @@ atomicshop/http_parse.py,sha256=nrf2rZcprLqtW8HVrV7TCZ1iTBcWRRy-mXIlAOzcaJs,9703
21
21
  atomicshop/inspect_wrapper.py,sha256=sGRVQhrJovNygHTydqJj0hxES-aB2Eg9KbIk3G31apw,11429
22
22
  atomicshop/ip_addresses.py,sha256=Hvi4TumEFoTEpKWaq5WNF-YzcRzt24IxmNgv-Mgax1s,1190
23
23
  atomicshop/keyboard_press.py,sha256=1W5kRtOB75fulVx-uF2yarBhW0_IzdI1k73AnvXstk0,452
24
+ atomicshop/on_exit.py,sha256=Wf1iy2e0b0Zu7oRxrct3VkLdQ_x9B32-z_JerKTt9Z0,5493
24
25
  atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erXXdjwtS4,4574
25
26
  atomicshop/permissions.py,sha256=P6tiUKV-Gw-c3ePEVsst9bqWaHJbB4ZlJB4xbDYVpEs,4436
26
27
  atomicshop/print_api.py,sha256=DhbCQd0MWZZ5GYEk4oTu1opRFC-b31g1VWZgTGewG2Y,11568
27
28
  atomicshop/process.py,sha256=Zgb4CUjy9gIBaawvtCOEcxGUCqvqPyARk0lpBjRzxWE,15950
28
- atomicshop/process_name_cmd.py,sha256=YmAfxZ3fhND58ItJ1fA1C6tPqyNUBlolOM2smKVDK2o,5167
29
- atomicshop/process_poller.py,sha256=Ya08tLe6NfbJ-U3EZWZ3j-FFqkDSy8zaTu1QeEsWC98,11754
29
+ atomicshop/process_name_cmd.py,sha256=CtaSp3mgxxJKCCVW8BLx6BJNx4giCklU_T7USiCEwfc,5162
30
+ atomicshop/process_poller.py,sha256=naqoq-gJN1kkaOm_MfoM7teIkh5OniXromVR4pb0pjY,11680
30
31
  atomicshop/python_file_patcher.py,sha256=kd3rBWvTcosLEk-7TycNdfKW9fZbe161iVwmH4niUo0,5515
31
32
  atomicshop/python_functions.py,sha256=zJg4ogUwECxrDD7xdDN5JikIUctITM5lsyabr_ZNsRw,4435
32
33
  atomicshop/question_answer_engine.py,sha256=DuOn7QEgKKfqZu2cR8mVeFIfFgayfBHiW-jY2VPq_Fo,841
@@ -79,7 +80,6 @@ atomicshop/archiver/zips.py,sha256=k742K1bEDtc_4N44j_Waebi-uOkxxavqltvV6q-BLW4,1
79
80
  atomicshop/basics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
81
  atomicshop/basics/ansi_escape_codes.py,sha256=WtIkm-BjSZS5J5irDUdAMBNvdX-qXFZcTX98jcBMpJE,3140
81
82
  atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
82
- atomicshop/basics/atexits.py,sha256=w7ge8sjuqK2_jdkdP4F4OCltkenQud8AhVEzxhFDiao,2145
83
83
  atomicshop/basics/booleans.py,sha256=va3LYIaSOhjdifW4ZEesnIQxBICNHyQjUAkYelzchhE,2047
84
84
  atomicshop/basics/bytes_arrays.py,sha256=WvSRDhIGt1ywF95t-yNgpxLm1nlZUbM1Dz6QckcyE8Y,5915
85
85
  atomicshop/basics/classes.py,sha256=EijW_g4EhdNBnKPMG3nT3HjFspTchtM7to6zm9Ad_Mk,9771
@@ -102,8 +102,10 @@ atomicshop/basics/threads.py,sha256=xvgdDJdmgN0wmmARoZ-H7Kvl1GOcEbvgaeGL4M3Hcx8,
102
102
  atomicshop/basics/timeit_template.py,sha256=fYLrk-X_dhdVtnPU22tarrhhvlggeW6FdKCXM8zkX68,405
103
103
  atomicshop/basics/tracebacks.py,sha256=cNfh_oAwF55kSIdqtv3boHZQIoQI8TajxkTnwJwpweI,535
104
104
  atomicshop/etw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
- atomicshop/etw/dns_trace.py,sha256=rv1A5akgebLzoT5EcvyuNwIztnA2xJYZlZlAbINFe88,5916
106
- atomicshop/etw/etw.py,sha256=WEs1aqXowH-tKUbaJphhc1hQvpi4SqU3ltyBg5wGM0c,4470
105
+ atomicshop/etw/providers.py,sha256=fVmWi-uGdtnsQTDpu_ty6dzx0GMhGokiST73LNBEJ38,129
106
+ atomicshop/etw/sessions.py,sha256=k3miewU278xn829cqDbsuH_bmZHPQE9-Zn-hINbxUSE,1330
107
+ atomicshop/etw/trace.py,sha256=tjBvV4RxCSn8Aoxj8NVxmqHekdECne_y4Zv2lbh11ak,5341
108
+ atomicshop/etw/trace_dns.py,sha256=f4homrWp4qMrmjC9UrEWjr9p9MrUg7SwOVbE22_IYgw,6728
107
109
  atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
110
  atomicshop/file_io/csvs.py,sha256=y8cJtnlN-NNxNupzJgSeGq9aQ4wNxYLFPX9vNNlUiIc,5830
109
111
  atomicshop/file_io/docxs.py,sha256=6tcYFGp0vRsHR47VwcRqwhdt2DQOwrAUYhrwN996n9U,5117
@@ -135,9 +137,8 @@ atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha
135
137
  atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
136
138
  atomicshop/monitor/change_monitor.py,sha256=dGhk5bJPxLCHa2FOVkort99E7vjVojra9GlvhpcKSqE,7551
137
139
  atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
138
- atomicshop/monitor/checks/dns.py,sha256=v3Pgdi8tmBmGAs7x7u6N5IiGyTv6GZOLEbrVSZZGE6s,7150
140
+ atomicshop/monitor/checks/dns.py,sha256=YLCFoolq35dnzdnBnfUA2ag-4HfvzcHfRdm3mzX8R8o,7184
139
141
  atomicshop/monitor/checks/file.py,sha256=2tIDSlX2KZNc_9i9ji1tcOqupbFTIOj7cKXLyBEDWMk,3263
140
- atomicshop/monitor/checks/hash.py,sha256=r4mDOdjItN3eyaJk8TVz03f4bQ_uKZ4MDTXagjseazs,2011
141
142
  atomicshop/monitor/checks/network.py,sha256=CGZWl4WlQrxayZeVF9JspJXwYA-zWx8ECWTVGSlXc98,3825
142
143
  atomicshop/monitor/checks/process_running.py,sha256=x66wd6-l466r8sbRQaIli0yswyGt1dH2DVXkGDL6O0Q,1891
143
144
  atomicshop/monitor/checks/url.py,sha256=1PvKt_d7wFg7rDMFpUejAQhj0mqWsmlmrNfjNAV2G4g,4123
@@ -168,6 +169,9 @@ atomicshop/wrappers/certauthw/certauth.py,sha256=hKedW0DOWlEigSNm8wu4SqHkCQsGJ1t
168
169
  atomicshop/wrappers/certauthw/certauthw.py,sha256=4WvhjANI7Kzqrr_nKmtA8Kf7B6rute_5wfP65gwQrjw,8082
169
170
  atomicshop/wrappers/ctyping/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
170
171
  atomicshop/wrappers/ctyping/process_winapi.py,sha256=QcXL-ETtlSSkoT8F7pYle97ubGWsjYp8cx8HxkVMgAc,2762
172
+ atomicshop/wrappers/ctyping/etw_winapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
+ atomicshop/wrappers/ctyping/etw_winapi/const.py,sha256=YrAyXBamAmHoQgha7oXwH9g_EqeLYXRGPderuu9FRI8,2765
174
+ atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py,sha256=3DLVXpTeOyTND35T_dKGzKnlLVQ0R3zt3AEcW2bNLNc,5304
171
175
  atomicshop/wrappers/ctyping/msi_windows_installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
176
  atomicshop/wrappers/ctyping/msi_windows_installer/base.py,sha256=Uu9SlWLsQQ6mjE-ek-ptHcmgiI3Ruah9bdZus70EaVY,4884
173
177
  atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py,sha256=htzwb2ROYI8yyc82xApStckPS2yCcoyaw32yC15KROs,3285
@@ -237,6 +241,7 @@ atomicshop/wrappers/psutilw/disks.py,sha256=3ZSVoommKH1TWo37j_83frB-NqXF4Nf5q5mB
237
241
  atomicshop/wrappers/psutilw/memories.py,sha256=_S0aL8iaoIHebd1vOFrY_T9aROM5Jx2D5CvDh_4j0Vc,528
238
242
  atomicshop/wrappers/psutilw/psutilw.py,sha256=G22ZQfGnqX15-feD8KUXfEZO4pFkIEnB8zgPzJ2jc7M,20868
239
243
  atomicshop/wrappers/psycopgw/psycopgw.py,sha256=XJvVf0oAUjCHkrYfKeFuGCpfn0Oxj3u4SbKMKA1508E,7118
244
+ atomicshop/wrappers/pywin32w/console.py,sha256=LstHajPLgXp9qQxFNR44QfH10nOnNp3bCJquxaTquns,1175
240
245
  atomicshop/wrappers/pywin32w/winshell.py,sha256=i2bKiMldPU7_azsD5xGQDdMwjaM7suKJd3k0Szmcs6c,723
241
246
  atomicshop/wrappers/pywin32w/wmi_win32process.py,sha256=qMzXtJ5hBZ5ydAyqpDbSx0nO2RJQL95HdmV5SzNKMhk,6826
242
247
  atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -255,8 +260,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
255
260
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
256
261
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
257
262
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
258
- atomicshop-2.12.23.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
259
- atomicshop-2.12.23.dist-info/METADATA,sha256=mDSQpG6SXbiytprFFrq70833uv_No7bDq5EtU_2q3xw,10479
260
- atomicshop-2.12.23.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
261
- atomicshop-2.12.23.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
262
- atomicshop-2.12.23.dist-info/RECORD,,
263
+ atomicshop-2.12.25.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
264
+ atomicshop-2.12.25.dist-info/METADATA,sha256=9B-DBn3RCaFkRPibGwrKm8FOiWIRdqjSifbGjXpi9z4,10479
265
+ atomicshop-2.12.25.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
266
+ atomicshop-2.12.25.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
267
+ atomicshop-2.12.25.dist-info/RECORD,,
@@ -1,64 +0,0 @@
1
- import atexit
2
- import signal
3
- import sys
4
- import platform
5
-
6
- from ..print_api import print_api
7
-
8
-
9
- def restart_function(callable_function, *args, **kwargs):
10
- """
11
- This function will run the callable function with the given arguments and keyword arguments.
12
- If the function raises an exception, the function will be restarted.
13
-
14
- :param callable_function: The function to run.
15
- :param args: The arguments to pass to the function.
16
- :param kwargs: The keyword arguments to pass to the function.
17
- :return: The return value of the function.
18
- """
19
- while True:
20
- try:
21
- return callable_function(*args, **kwargs)
22
- except Exception as e:
23
- print(f"ERROR: {e}")
24
- continue
25
-
26
-
27
- def run_callable_on_exit_and_signals(
28
- callable_function,
29
- print_kwargs: dict = None,
30
- *args,
31
- **kwargs
32
- ):
33
- """
34
- This function will run the callable function with the given arguments and keyword arguments.
35
- If the function raises an exception, the function will be restarted.
36
-
37
- :param callable_function: The function to run.
38
- :param print_kwargs: print_api kwargs.
39
- :param args: The arguments to pass to the function.
40
- :param kwargs: The keyword arguments to pass to the function.
41
- :return: The return value of the function.
42
- """
43
- def signal_handler(signum, frame):
44
- print_api(f"Signal {signum} received, exiting.", **(print_kwargs or {}))
45
- callable_function(*args, **kwargs)
46
- input("Press Enter to exit.")
47
- sys.exit(0)
48
-
49
- def exit_handler():
50
- print_api("Exiting.", **(print_kwargs or {}))
51
- callable_function(*args, **kwargs)
52
- input("Press Enter to exit.")
53
- sys.exit(0)
54
-
55
- signals = [signal.SIGINT, signal.SIGTERM]
56
- if platform.system() != 'Windows':
57
- signals.append(signal.SIGQUIT)
58
- signals.append(signal.SIGHUP)
59
- for sig in signals:
60
- signal.signal(sig, signal_handler)
61
-
62
- # signal.signal(signal.SIGINT, signal_handler)
63
- # signal.signal(signal.SIGTERM, signal_handler)
64
- atexit.register(exit_handler)
@@ -1,56 +0,0 @@
1
- from ... import diff_check
2
- from ...print_api import print_api
3
- from .hash_checks import file, url
4
-
5
-
6
- DIFF_CHECKER = diff_check.DiffChecker(
7
- return_first_cycle=False,
8
- operation_type='single_object'
9
- )
10
-
11
-
12
- def setup_check(change_monitor_instance):
13
- change_monitor_instance.set_input_file_path()
14
-
15
- if change_monitor_instance.object_type == 'file':
16
- file.setup_check(change_monitor_instance, change_monitor_instance.check_object)
17
- elif 'url_' in change_monitor_instance.object_type:
18
- url.setup_check(change_monitor_instance, change_monitor_instance.check_object)
19
-
20
-
21
- def execute_cycle(change_monitor_instance, print_kwargs: dict = None):
22
- """
23
- This function executes the cycle of the change monitor: hash.
24
-
25
- :param change_monitor_instance: Instance of the ChangeMonitor class.
26
- :param print_kwargs: print_api kwargs.
27
-
28
- :return: List of dictionaries with the results of the cycle.
29
- """
30
-
31
- if print_kwargs is None:
32
- print_kwargs = dict()
33
-
34
- return_list = list()
35
-
36
- if change_monitor_instance.object_type == 'file':
37
- # Get the hash of the object.
38
- file.get_hash(change_monitor_instance, change_monitor_instance.check_object, print_kwargs=print_kwargs)
39
- elif 'url_' in change_monitor_instance.object_type:
40
- # Get the hash of the object.
41
- url.get_hash(change_monitor_instance, change_monitor_instance.check_object, print_kwargs=print_kwargs)
42
-
43
- # Check if the object was updated.
44
- result, message = change_monitor_instance.diff_checker.check_string(
45
- print_kwargs=print_kwargs)
46
-
47
- # If the object was updated, print the message in yellow color, otherwise print in green color.
48
- if result:
49
- print_api(message, color='yellow', **print_kwargs)
50
- # create_message_file(message, self.__class__.__name__, logger=self.logger)
51
-
52
- return_list.append(message)
53
- else:
54
- print_api(message, color='green', **print_kwargs)
55
-
56
- return return_list