atomicshop 2.14.0__py3-none-any.whl → 2.14.2__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.14.0'
4
+ __version__ = '2.14.2'
atomicshop/etws/trace.py CHANGED
@@ -27,8 +27,7 @@ class EventTrace(etw.ETW):
27
27
  session_name: str = None,
28
28
  close_existing_session_name: bool = True,
29
29
  enable_process_poller: bool = False,
30
- process_poller_etw_session_name: str = None,
31
- process_poller_method: Literal['psutil', 'pywin32', 'process_dll', 'sysmon_etw'] = 'sysmon_etw'
30
+ process_poller_method: Literal['psutil', 'pywin32', 'process_dll', 'sysmon_etw', 'event_log'] = 'event_log'
32
31
  ):
33
32
  """
34
33
  :param providers: List of tuples with provider name and provider GUID.
@@ -42,13 +41,13 @@ class EventTrace(etw.ETW):
42
41
  :param enable_process_poller: Boolean to enable process poller. Gets the process PID, Name and CommandLine.
43
42
  Since the DNS events doesn't contain the process name and command line, only PID.
44
43
  Then DNS events will be enriched with the process name and command line from the process poller.
45
- :param process_poller_etw_session_name: The name of the ETW session for tracing process creation.
46
44
  :param process_poller_method: The method to get the process information. For more information, see the
47
45
  'process_poller.ProcessPollerPool' class. Summary:
48
46
  'psutil': Uses 'psutil' library to get the process information.
49
47
  'pywin32': Uses 'pywin32' library to get the process information.
50
48
  'process_dll': Uses 'process' custom DLL to get the process information.
51
49
  'sysmon_etw': Uses 'sysmon_etw' uses sysmon and ETW to get the process information.
50
+ 'event_log': Uses Security Windows EVent Log channel (event id 4688) to get the process information.
52
51
 
53
52
  ------------------------------------------
54
53
 
@@ -72,7 +71,6 @@ class EventTrace(etw.ETW):
72
71
  self.event_queue = queue.Queue()
73
72
  self.close_existing_session_name: bool = close_existing_session_name
74
73
  self.enable_process_poller: bool = enable_process_poller
75
- self.process_poller_etw_session_name: str = process_poller_etw_session_name
76
74
 
77
75
  # If no callback function is provided, we will use the default one, which will put the event in the queue.
78
76
  if not event_callback:
@@ -86,7 +84,8 @@ class EventTrace(etw.ETW):
86
84
  for provider in providers:
87
85
  etw_format_providers.append(etw.ProviderInfo(provider[0], etw.GUID(provider[1])))
88
86
 
89
- if not process_poller_etw_session_name:
87
+ process_poller_etw_session_name = None
88
+ if process_poller_method == 'sysmon_etw':
90
89
  process_poller_etw_session_name = PROCESS_POLLER_ETW_DEFAULT_SESSION_NAME
91
90
 
92
91
  if self.enable_process_poller:
@@ -24,8 +24,7 @@ class DnsRequestResponseTrace:
24
24
  self,
25
25
  attrs: list = None,
26
26
  session_name: str = None,
27
- close_existing_session_name: bool = True,
28
- process_poller_etw_session_name: str = None
27
+ close_existing_session_name: bool = True
29
28
  ):
30
29
  """
31
30
  :param attrs: List of attributes to return. If None, all attributes will be returned.
@@ -36,7 +35,6 @@ class DnsRequestResponseTrace:
36
35
  False: if ETW session with 'session_name' exists, you will be notified and the new session will not be
37
36
  created. Instead, the existing session will be used. If there is a buffer from the previous session,
38
37
  you will get the events from the buffer.
39
- :param process_poller_etw_session_name: The name of the ETW session for tracing process creation.
40
38
 
41
39
  -------------------------------------------------
42
40
 
@@ -48,8 +46,7 @@ class DnsRequestResponseTrace:
48
46
  attrs=['pid', 'name', 'cmdline', 'domain', 'query_type'],
49
47
  session_name='MyDnsTrace',
50
48
  close_existing_session_name=True,
51
- enable_process_poller=True,
52
- process_poller_etw_session_name='MyProcessTrace'
49
+ enable_process_poller=True
53
50
  )
54
51
  dns_trace_w.start()
55
52
  while True:
@@ -69,8 +66,7 @@ class DnsRequestResponseTrace:
69
66
  event_id_filters=[REQUEST_RESP_EVENT_ID],
70
67
  session_name=session_name,
71
68
  close_existing_session_name=close_existing_session_name,
72
- enable_process_poller=True,
73
- process_poller_etw_session_name=process_poller_etw_session_name
69
+ enable_process_poller=True
74
70
  )
75
71
 
76
72
  def start(self):
@@ -41,8 +41,7 @@ class ChangeMonitor:
41
41
  'url_playwright_jpeg'],
42
42
  None] = None,
43
43
  object_type_settings: dict = None,
44
- etw_session_name: str = None,
45
- etw_process_session_name: str = None
44
+ etw_session_name: str = None
46
45
  ):
47
46
  """
48
47
  :param object_type: string, type of object to check. The type must be one of the following:
@@ -89,8 +88,6 @@ class ChangeMonitor:
89
88
  with logman and other tools: logman query -ets
90
89
  If not provided, a default name will be generated.
91
90
  'dns': 'AtomicShopDnsTrace'
92
- :param etw_process_session_name: string, the name of the ETW session for tracing process creation.
93
- This is needed to correlate the process cmd with the DNS requests PIDs.
94
91
 
95
92
  If 'input_directory' is not specified, the 'input_file_name' is not specified, and
96
93
  'generate_input_file_name' is False, then the input file will not be used and the object will be stored
@@ -107,7 +104,6 @@ class ChangeMonitor:
107
104
  self.object_type = object_type
108
105
  self.object_type_settings: dict = object_type_settings
109
106
  self.etw_session_name: str = etw_session_name
110
- self.etw_process_session_name: str = etw_process_session_name
111
107
 
112
108
  # === Additional variables ========================================
113
109
 
@@ -27,8 +27,7 @@ class DnsCheck:
27
27
  trace_dns.DnsRequestResponseTrace(
28
28
  attrs=['name', 'cmdline', 'domain', 'query_type'],
29
29
  session_name=self.etw_session_name,
30
- close_existing_session_name=True,
31
- process_poller_etw_session_name=change_monitor_instance.etw_process_session_name
30
+ close_existing_session_name=True
32
31
  )
33
32
  )
34
33
 
@@ -2,8 +2,10 @@ import threading
2
2
  import multiprocessing
3
3
  import time
4
4
  from typing import Literal, Union
5
+ from pathlib import Path
5
6
 
6
7
  from .wrappers.pywin32w import wmi_win32process
8
+ from .wrappers.pywin32w.win_event_log.subscribes import process_create
7
9
  from .wrappers.psutilw import psutilw
8
10
  from .etws.traces import trace_sysmon_process_creation
9
11
  from .basics import dicts
@@ -52,7 +54,7 @@ class GetProcessList:
52
54
  """
53
55
  def __init__(
54
56
  self,
55
- get_method: Literal['psutil', 'pywin32', 'process_dll', 'sysmon_etw'] = 'process_dll',
57
+ get_method: Literal['psutil', 'pywin32', 'process_dll'] = 'process_dll',
56
58
  connect_on_init: bool = False
57
59
  ):
58
60
  """
@@ -145,7 +147,7 @@ class ProcessPollerPool:
145
147
  self,
146
148
  interval_seconds: Union[int, float] = 0,
147
149
  operation: Literal['thread', 'process'] = 'thread',
148
- poller_method: Literal['psutil', 'pywin32', 'process_dll', 'sysmon_etw'] = 'sysmon_etw',
150
+ poller_method: Literal['psutil', 'pywin32', 'process_dll', 'sysmon_etw', 'event_log'] = 'event_log',
149
151
  sysmon_etw_session_name: str = None,
150
152
  sysmon_directory: str = None
151
153
  ):
@@ -175,6 +177,9 @@ class ProcessPollerPool:
175
177
  2. Start the "Microsoft-Windows-Sysmon" ETW session.
176
178
  3. Take a snapshot of current processes and their CMDs with psutil and store it in a dict.
177
179
  4. Each new process creation from ETW updates the dict.
180
+ 'event_log': Get the list of processes by subscribing to the Windows Event Log.
181
+ Log Channel: Security, Event ID: 4688.
182
+ We enable the necessary prerequisites in registry and subscribe to the event.
178
183
  :param sysmon_etw_session_name: str, only for 'sysmon_etw' get_method.
179
184
  The name of the ETW session for tracing process creation.
180
185
  :param sysmon_directory: str, only for 'sysmon_etw' get_method.
@@ -271,6 +276,12 @@ def _worker(
271
276
  # We must initiate the connection inside the thread/process, because it is not thread-safe.
272
277
  poller_instance.start()
273
278
 
279
+ processes = GetProcessList(get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
280
+ process_queue.put(processes)
281
+ elif poller_method == 'event_log':
282
+ poller_instance = process_create.ProcessCreateSubscriber()
283
+ poller_instance.start()
284
+
274
285
  processes = GetProcessList(get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
275
286
  process_queue.put(processes)
276
287
  else:
@@ -279,7 +290,6 @@ def _worker(
279
290
  processes = {}
280
291
 
281
292
  exception = None
282
- list_of_processes: list = list()
283
293
  while running_state:
284
294
  try:
285
295
  if poller_method == 'sysmon_etw':
@@ -289,6 +299,13 @@ def _worker(
289
299
  'name': current_cycle['original_file_name'],
290
300
  'cmdline': current_cycle['command_line']}
291
301
  }
302
+ elif poller_method == 'event_log':
303
+ # Get the current processes and reinitialize the instance of the dict.
304
+ current_cycle: dict = poller_instance.emit()
305
+ current_processes: dict = {current_cycle['pid']: {
306
+ 'name': Path(current_cycle['process_name']).name,
307
+ 'cmdline': current_cycle['command_line']}
308
+ }
292
309
  else:
293
310
  # Get the current processes and reinitialize the instance of the dict.
294
311
  current_processes: dict = dict(poller_instance.get_processes())
@@ -312,8 +329,8 @@ def _worker(
312
329
 
313
330
  process_queue.put(processes)
314
331
 
315
- # Since ETW is a blocking operation, we don't need to sleep.
316
- if poller_method != 'sysmon_etw':
332
+ # Since ETW is a blocking operation, we don't need to sleep in tracing pollers [sysmon_etw, event_log].
333
+ if poller_method not in ['sysmon_etw', 'event_log']:
317
334
  time.sleep(interval_seconds)
318
335
  except KeyboardInterrupt as e:
319
336
  running_state = False
@@ -4,6 +4,7 @@ import time
4
4
  import threading
5
5
  import queue
6
6
  from typing import Union
7
+ import binascii
7
8
 
8
9
 
9
10
  class EventLogSubscriber:
@@ -20,15 +21,29 @@ class EventLogSubscriber:
20
21
  event = event_log_subscriber.emit()
21
22
  print(event)
22
23
  """
23
- def __init__(self, log_channel: str, event_id: int):
24
+ def __init__(self, log_channel: str, event_id: int = None, provider: str = None):
24
25
  """
25
26
  :param log_channel: The name of the event log channel to subscribe to. Examples:
26
27
  Security, System, Application, etc.
27
28
  :param event_id: The ID of the event to subscribe to.
28
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.
29
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
+
30
40
  self.log_channel: str = log_channel
31
- self.event_id: str = str(event_id)
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
32
47
 
33
48
  self._event_queue = queue.Queue()
34
49
  self._subscription_thread = None
@@ -36,7 +51,7 @@ class EventLogSubscriber:
36
51
  def start(self):
37
52
  """Start the subscription process."""
38
53
  self._subscription_thread = threading.Thread(
39
- target=start_subscription, args=(self.log_channel, self.event_id, self._event_queue)
54
+ target=start_subscription, args=(self.log_channel, self._event_queue, self.event_id, self.provider)
40
55
  )
41
56
  self._subscription_thread.daemon = True
42
57
  self._subscription_thread.start()
@@ -64,51 +79,83 @@ class EventLogSubscriber:
64
79
  def _parse_event_xml(event_xml):
65
80
  root = Et.fromstring(event_xml)
66
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
67
88
  for elem in root.iter():
68
- if 'Name' in elem.attrib:
69
- data[elem.attrib['Name']] = elem.text
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
+
70
144
  return data
71
145
 
72
146
 
73
147
  def _handle_event(event, event_queue):
148
+ # Render event as XML
74
149
  event_xml = win32evtlog.EvtRender(event, win32evtlog.EvtRenderEventXml)
150
+ data = None
75
151
  try:
76
152
  data = _parse_event_xml(event_xml)
77
153
  except Et.ParseError as e:
78
154
  print(f"Error parsing event XML: {e}")
79
- return
80
-
81
- event_dict: dict = {
82
- 'user_sid': data.get("SubjectUserSid", "Unknown"),
83
- 'user_name': data.get("SubjectUserName", "Unknown"),
84
- 'domain': data.get("SubjectDomainName", "Unknown"),
85
- 'pid_hex': data.get("NewProcessId", "0"),
86
- 'process_name': data.get("NewProcessName", "Unknown"),
87
- 'command_line': data.get("CommandLine", None),
88
- 'parent_pid_hex': data.get("ProcessId", "0"),
89
- 'parent_process_name': data.get("ParentProcessName", "Unknown")
90
- }
91
-
92
- try:
93
- process_id = int(event_dict['pid_hex'], 16)
94
- except ValueError:
95
- process_id = "Unknown"
155
+ except Exception as e:
156
+ print(f"Error getting rendered message: {e}")
96
157
 
97
- try:
98
- parent_pid = int(event_dict['parent_pid_hex'], 16)
99
- except ValueError:
100
- parent_pid = "Unknown"
101
-
102
- event_dict['pid'] = process_id
103
- event_dict['parent_pid'] = parent_pid
104
-
105
- # if user_sid != "Unknown":
106
- # try:
107
- # user_name, domain, type = win32security.LookupAccountSid(None, user_sid)
108
- # except Exception as e:
109
- # print(f"Error looking up account SID: {e}")
110
-
111
- event_queue.put(event_dict)
158
+ event_queue.put(data)
112
159
 
113
160
 
114
161
  def _event_callback(action, context, event):
@@ -117,25 +164,43 @@ def _event_callback(action, context, event):
117
164
  _handle_event(event, event_queue)
118
165
 
119
166
 
120
- def start_subscription(log_channel: str, event_id: int, event_queue):
167
+ def start_subscription(
168
+ log_channel: str,
169
+ event_queue,
170
+ event_id: str = None,
171
+ provider: str = None
172
+ ):
121
173
  """
122
174
  Start listening for events in the specified log channel with the given event ID.
123
175
 
124
176
  :param log_channel: The name of the event log channel to subscribe to. Examples:
125
177
  Security, System, Application, etc.
178
+ :param event_queue: A queue to store the received events
126
179
  :param event_id: The ID of the event to subscribe to.
127
180
  Example: 4688 for process creation events in "Security" channel.
128
- :param event_queue: A queue to store the received events
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.
129
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
+
130
191
  # This selects the System node within each event.
131
192
  # The System node contains metadata about the event, such as the event ID, provider name, timestamp, and more.
132
- query = f"*[System/EventID={str(event_id)}]"
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}]"
133
198
 
134
199
  subscription = win32evtlog.EvtSubscribe(
135
200
  log_channel,
136
201
  win32evtlog.EvtSubscribeToFutureEvents,
137
202
  SignalEvent=None,
138
- Query=query,
203
+ Query=xpath_query,
139
204
  Callback=_event_callback,
140
205
  Context={'event_queue': event_queue}
141
206
  )
@@ -7,6 +7,8 @@ from .....print_api import print_api
7
7
 
8
8
  AUDITING_REG_PATH: str = r"Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit"
9
9
  PROCESS_CREATION_INCLUDE_CMDLINE_VALUE: str = "ProcessCreationIncludeCmdLine_Enabled"
10
+ LOG_CHANNEL: str = 'Security'
11
+ EVENT_ID: int = 4688
10
12
 
11
13
 
12
14
  class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
@@ -14,9 +16,9 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
14
16
  Class for subscribing to Windows Event Log events related to process creation.
15
17
 
16
18
  Usage:
17
- from atomicshop.wrappers.pywin32w.win_event_log.subscribes import subscribe_to_process_create
19
+ from atomicshop.wrappers.pywin32w.win_event_log.subscribes import process_create
18
20
 
19
- process_create_subscriber = subscribe_to_process_create.ProcessCreateSubscriber()
21
+ process_create_subscriber = process_create.ProcessCreateSubscriber()
20
22
  process_create_subscriber.start()
21
23
 
22
24
  while True:
@@ -24,7 +26,7 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
24
26
  print(event)
25
27
  """
26
28
  def __init__(self):
27
- super().__init__('Security', 4688)
29
+ super().__init__(log_channel=LOG_CHANNEL, event_id=EVENT_ID)
28
30
 
29
31
  def start(self):
30
32
  """Start the subscription process."""
@@ -45,7 +47,40 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
45
47
  If None, the function will block until an event is available.
46
48
  :return: A dictionary containing the event data.
47
49
  """
48
- return super().emit(timeout=timeout)
50
+
51
+ data = super().emit(timeout=timeout)
52
+
53
+ event_dict: dict = {
54
+ 'user_sid': data.get("SubjectUserSid", "Unknown"),
55
+ 'user_name': data.get("SubjectUserName", "Unknown"),
56
+ 'domain': data.get("SubjectDomainName", "Unknown"),
57
+ 'pid_hex': data.get("NewProcessId", "0"),
58
+ 'process_name': data.get("NewProcessName", "Unknown"),
59
+ 'command_line': data.get("CommandLine", None),
60
+ 'parent_pid_hex': data.get("ProcessId", "0"),
61
+ 'parent_process_name': data.get("ParentProcessName", "Unknown")
62
+ }
63
+
64
+ try:
65
+ process_id = int(event_dict['pid_hex'], 16)
66
+ except ValueError:
67
+ process_id = "Unknown"
68
+
69
+ try:
70
+ parent_pid = int(event_dict['parent_pid_hex'], 16)
71
+ except ValueError:
72
+ parent_pid = "Unknown"
73
+
74
+ event_dict['pid'] = process_id
75
+ event_dict['parent_pid'] = parent_pid
76
+
77
+ # if user_sid != "Unknown":
78
+ # try:
79
+ # user_name, domain, type = win32security.LookupAccountSid(None, user_sid)
80
+ # except Exception as e:
81
+ # print(f"Error looking up account SID: {e}")
82
+
83
+ return event_dict
49
84
 
50
85
 
51
86
  def enable_audit_process_creation(print_kwargs: dict = None):
@@ -91,10 +126,10 @@ def is_audit_process_creation_enabled(print_kwargs: dict = None) -> bool:
91
126
  # color='green', **(print_kwargs or {}))
92
127
  return True
93
128
  else:
94
- print_api(output, **(print_kwargs or {}))
95
- print_api(
96
- "'Audit Process Creation' is not fully enabled. Check the output above for details.",
97
- color='yellow', **(print_kwargs or {}))
129
+ # print_api(output, **(print_kwargs or {}))
130
+ # print_api(
131
+ # "'Audit Process Creation' is not fully enabled. Check the output above for details.",
132
+ # color='yellow', **(print_kwargs or {}))
98
133
  return False
99
134
  except subprocess.CalledProcessError as e:
100
135
  print_api(f"Failed to check 'Audit Process Creation': {e}", color='red', error_type=True, **(print_kwargs or {}))
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.14.0
3
+ Version: 2.14.2
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=LlDA7S0dPFwG14ZFuGv5tyUVyow2__rgMRanlcYHT88,123
1
+ atomicshop/__init__.py,sha256=z4rThLLSu1i3Wlh2xQO9kTZiAnom3boVcidQ2m4F8V0,123
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
@@ -27,7 +27,7 @@ atomicshop/permissions.py,sha256=P6tiUKV-Gw-c3ePEVsst9bqWaHJbB4ZlJB4xbDYVpEs,443
27
27
  atomicshop/print_api.py,sha256=DhbCQd0MWZZ5GYEk4oTu1opRFC-b31g1VWZgTGewG2Y,11568
28
28
  atomicshop/process.py,sha256=R1BtXWjG2g2Q3WlsyhbIlXZz0UkQeagY7fQyBOIX_DM,15951
29
29
  atomicshop/process_name_cmd.py,sha256=CtaSp3mgxxJKCCVW8BLx6BJNx4giCklU_T7USiCEwfc,5162
30
- atomicshop/process_poller.py,sha256=sGIiLNdYdyxdER7hKP8QcbYg1esKe5ymCfh5qUATpJM,15033
30
+ atomicshop/process_poller.py,sha256=17sc332AcTfSVPorjYsgRwNm75rLCqvpOQsieMwLMKU,16105
31
31
  atomicshop/python_file_patcher.py,sha256=kd3rBWvTcosLEk-7TycNdfKW9fZbe161iVwmH4niUo0,5515
32
32
  atomicshop/python_functions.py,sha256=zJg4ogUwECxrDD7xdDN5JikIUctITM5lsyabr_ZNsRw,4435
33
33
  atomicshop/question_answer_engine.py,sha256=DuOn7QEgKKfqZu2cR8mVeFIfFgayfBHiW-jY2VPq_Fo,841
@@ -106,9 +106,9 @@ atomicshop/etws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  atomicshop/etws/const.py,sha256=v3x_IdCYeSKbCGywiZFOZln80ldpwKW5nuMDuUe51Jg,1257
107
107
  atomicshop/etws/providers.py,sha256=fVmWi-uGdtnsQTDpu_ty6dzx0GMhGokiST73LNBEJ38,129
108
108
  atomicshop/etws/sessions.py,sha256=k3miewU278xn829cqDbsuH_bmZHPQE9-Zn-hINbxUSE,1330
109
- atomicshop/etws/trace.py,sha256=H0uDB6I86UoNyijE5HCukp_paJNHTjKL6eAzBRGSI50,8509
109
+ atomicshop/etws/trace.py,sha256=v2yA3FicR9WIg5n5KlZEuYbLAzTTjiDk7avWxcEjwp8,8439
110
110
  atomicshop/etws/traces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
- atomicshop/etws/traces/trace_dns.py,sha256=8Fl5UW4An3SE8tGXGTufTgOcWa574YZ6zQbU7x5DYrs,6270
111
+ atomicshop/etws/traces/trace_dns.py,sha256=-bw7JGDeAl2UtNGoSPeD_gh6ij4IGAbPdB8VWip8fXc,5960
112
112
  atomicshop/etws/traces/trace_sysmon_process_creation.py,sha256=WdlQiOfRZC-_PpuE6BkOw5zB0DLc3fhVrANyLrgfCac,4833
113
113
  atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  atomicshop/file_io/csvs.py,sha256=y8cJtnlN-NNxNupzJgSeGq9aQ4wNxYLFPX9vNNlUiIc,5830
@@ -139,9 +139,9 @@ atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256
139
139
  atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha256=KENDVf9OwXD9gwSh4B1XxACCe7iHYjrvnW1t6F64wdE,695
140
140
  atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=1AM49UaFTKA0AHw-k3SV3uH3QbG-o6ux0c-GoWkKNU0,6993
141
141
  atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
- atomicshop/monitor/change_monitor.py,sha256=eJRP7NBnA-Y_n6Ofm_jKrR5XguJV6gEmxFdtdGMBF3k,7866
142
+ atomicshop/monitor/change_monitor.py,sha256=dGhk5bJPxLCHa2FOVkort99E7vjVojra9GlvhpcKSqE,7551
143
143
  atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
- atomicshop/monitor/checks/dns.py,sha256=OJWvMKl0zNtvBTYVYHSK_tfH0cNxCJ1kc_O-2Ob_5fc,7137
144
+ atomicshop/monitor/checks/dns.py,sha256=clslElMi2HZaO3G1nXt2t0O2Yj0vFsJd6CXRdIXCGJM,7038
145
145
  atomicshop/monitor/checks/file.py,sha256=2tIDSlX2KZNc_9i9ji1tcOqupbFTIOj7cKXLyBEDWMk,3263
146
146
  atomicshop/monitor/checks/network.py,sha256=CGZWl4WlQrxayZeVF9JspJXwYA-zWx8ECWTVGSlXc98,3825
147
147
  atomicshop/monitor/checks/process_running.py,sha256=x66wd6-l466r8sbRQaIli0yswyGt1dH2DVXkGDL6O0Q,1891
@@ -251,9 +251,10 @@ atomicshop/wrappers/pywin32w/console.py,sha256=LstHajPLgXp9qQxFNR44QfH10nOnNp3bC
251
251
  atomicshop/wrappers/pywin32w/winshell.py,sha256=i2bKiMldPU7_azsD5xGQDdMwjaM7suKJd3k0Szmcs6c,723
252
252
  atomicshop/wrappers/pywin32w/wmi_win32process.py,sha256=qMzXtJ5hBZ5ydAyqpDbSx0nO2RJQL95HdmV5SzNKMhk,6826
253
253
  atomicshop/wrappers/pywin32w/win_event_log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py,sha256=UztWltQPK_fQ3EWyY6tGfhAqwxDjK7RVIAZppu97rKI,5104
254
+ atomicshop/wrappers/pywin32w/win_event_log/subscribe.py,sha256=a0SqjtUfo-fVR5jlsoygFlDrjHelOKgCBvia3suyzW8,8217
255
255
  atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/subscribe_to_process_create.py,sha256=_rMCTaREi_PJxAPP4gAB0IW9F0XO-suUXLf97tboKSc,5594
256
+ atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py,sha256=_no1MnXjhLgOi9Y7xc7HHgTe6sjZkHIIhcVCd5BKZgM,6865
257
+ atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py,sha256=8nxIcNcbeEuvoBwhujgh7-oIpL9A6J-gg1NM8hOGAVA,3442
257
258
  atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
258
259
  atomicshop/wrappers/socketw/accepter.py,sha256=HQC1EyZmyUtVEfFbaBkHCE-VZp6RWyd9mEqAkgsE1fk,1749
259
260
  atomicshop/wrappers/socketw/base.py,sha256=1vvg8EhRGvnxdrRAm1VJSLCXkm2SZDHRjdpTuhkH3Mg,1844
@@ -270,8 +271,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
270
271
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
271
272
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
272
273
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
273
- atomicshop-2.14.0.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
274
- atomicshop-2.14.0.dist-info/METADATA,sha256=M4vV-yH5er5NVOQJMUtIPqdgh6IHzINAY1frKcRF9oU,10478
275
- atomicshop-2.14.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
276
- atomicshop-2.14.0.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
277
- atomicshop-2.14.0.dist-info/RECORD,,
274
+ atomicshop-2.14.2.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
275
+ atomicshop-2.14.2.dist-info/METADATA,sha256=VIgp-Go_u3KNgCb_tRAE6XcD9IyNa2lHdn0C2WFmscY,10478
276
+ atomicshop-2.14.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
277
+ atomicshop-2.14.2.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
278
+ atomicshop-2.14.2.dist-info/RECORD,,