atomicshop 2.13.1__py3-none-any.whl → 2.14.0__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.13.1'
4
+ __version__ = '2.14.0'
atomicshop/etws/trace.py CHANGED
@@ -1,11 +1,21 @@
1
1
  import queue
2
2
  import sys
3
+ import time
4
+ from typing import Literal
3
5
 
4
6
  # Import FireEye Event Tracing library.
5
7
  import etw
6
8
 
7
9
  from ..print_api import print_api
8
10
  from . import sessions
11
+ from .. import process_poller
12
+ from ..wrappers.psutilw import psutilw
13
+
14
+
15
+ PROCESS_POLLER_ETW_DEFAULT_SESSION_NAME: str = 'AtomicShopProcessTrace'
16
+
17
+ WAIT_FOR_PROCESS_POLLER_PID_SECONDS: int = 3
18
+ WAIT_FOR_PROCESS_POLLER_PID_COUNTS: int = WAIT_FOR_PROCESS_POLLER_PID_SECONDS * 10
9
19
 
10
20
 
11
21
  class EventTrace(etw.ETW):
@@ -15,7 +25,10 @@ class EventTrace(etw.ETW):
15
25
  event_callback=None,
16
26
  event_id_filters: list = None,
17
27
  session_name: str = None,
18
- close_existing_session_name: bool = True
28
+ close_existing_session_name: bool = True,
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'
19
32
  ):
20
33
  """
21
34
  :param providers: List of tuples with provider name and provider GUID.
@@ -26,7 +39,19 @@ class EventTrace(etw.ETW):
26
39
  The default in the 'etw.ETW' method is 'None'.
27
40
  :param session_name: The name of the session to create. If not provided, a UUID will be generated.
28
41
  :param close_existing_session_name: Boolean to close existing session names.
42
+ :param enable_process_poller: Boolean to enable process poller. Gets the process PID, Name and CommandLine.
43
+ Since the DNS events doesn't contain the process name and command line, only PID.
44
+ 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
+ :param process_poller_method: The method to get the process information. For more information, see the
47
+ 'process_poller.ProcessPollerPool' class. Summary:
48
+ 'psutil': Uses 'psutil' library to get the process information.
49
+ 'pywin32': Uses 'pywin32' library to get the process information.
50
+ 'process_dll': Uses 'process' custom DLL to get the process information.
51
+ 'sysmon_etw': Uses 'sysmon_etw' uses sysmon and ETW to get the process information.
52
+
29
53
  ------------------------------------------
54
+
30
55
  You should stop the ETW tracing when you are done with it.
31
56
  'pywintrace' module starts a new session for ETW tracing, and it will not stop the session when the script
32
57
  exits or exception is raised.
@@ -46,6 +71,8 @@ class EventTrace(etw.ETW):
46
71
  """
47
72
  self.event_queue = queue.Queue()
48
73
  self.close_existing_session_name: bool = close_existing_session_name
74
+ self.enable_process_poller: bool = enable_process_poller
75
+ self.process_poller_etw_session_name: str = process_poller_etw_session_name
49
76
 
50
77
  # If no callback function is provided, we will use the default one, which will put the event in the queue.
51
78
  if not event_callback:
@@ -59,12 +86,23 @@ class EventTrace(etw.ETW):
59
86
  for provider in providers:
60
87
  etw_format_providers.append(etw.ProviderInfo(provider[0], etw.GUID(provider[1])))
61
88
 
89
+ if not process_poller_etw_session_name:
90
+ process_poller_etw_session_name = PROCESS_POLLER_ETW_DEFAULT_SESSION_NAME
91
+
92
+ if self.enable_process_poller:
93
+ self.process_poller = process_poller.ProcessPollerPool(
94
+ operation='process', poller_method=process_poller_method,
95
+ sysmon_etw_session_name=process_poller_etw_session_name)
96
+
62
97
  super().__init__(
63
98
  providers=etw_format_providers, event_callback=function_callable, event_id_filters=event_id_filters,
64
99
  session_name=session_name
65
100
  )
66
101
 
67
102
  def start(self):
103
+ if self.enable_process_poller:
104
+ self.process_poller.start()
105
+
68
106
  # Check if the session name already exists.
69
107
  if sessions.is_session_running(self.session_name):
70
108
  print_api(f'ETW Session already running: {self.session_name}', color='yellow')
@@ -87,6 +125,9 @@ class EventTrace(etw.ETW):
87
125
  def stop(self):
88
126
  super().stop()
89
127
 
128
+ if self.enable_process_poller:
129
+ self.process_poller.stop()
130
+
90
131
  def emit(self):
91
132
  """
92
133
  The Function will return the next event from the queue.
@@ -104,34 +145,44 @@ class EventTrace(etw.ETW):
104
145
  :return: etw event object.
105
146
  """
106
147
 
107
- return self.event_queue.get()
108
-
109
-
110
- def find_sessions_by_provider(provider_name: str):
111
- """
112
- Find ETW session by provider name.
113
-
114
- :param provider_name: The name of the provider to search for.
115
- """
148
+ # Get the processes first, since we need the process name and command line.
149
+ # If they're not ready, we will get just pids from DNS tracing.
150
+ if self.enable_process_poller:
151
+ self._get_processes_from_poller()
116
152
 
117
- return
153
+ event: tuple = self.event_queue.get()
118
154
 
155
+ event_dict: dict = {
156
+ 'event_id': event[0],
157
+ 'event': event[1],
158
+ 'pid': event[1]['EventHeader']['ProcessId']
159
+ }
119
160
 
120
- def get_all_providers_from_session(session_name: str):
121
- """
122
- Get all providers that ETW session uses.
161
+ if self.enable_process_poller:
162
+ processes = self.process_poller.get_processes()
163
+ if event_dict['pid'] not in processes:
164
+ counter = 0
165
+ while counter < WAIT_FOR_PROCESS_POLLER_PID_COUNTS:
166
+ processes = self.process_poller.get_processes()
167
+ if event_dict['pid'] not in processes:
168
+ time.sleep(0.1)
169
+ counter += 1
170
+ else:
171
+ break
123
172
 
124
- :param session_name: The name of the session to get providers from.
125
- """
173
+ if counter == WAIT_FOR_PROCESS_POLLER_PID_COUNTS:
174
+ print_api(f"Error: Couldn't get the process name for PID: {event_dict['pid']}.", color='red')
126
175
 
127
- return
176
+ event_dict = psutilw.cross_single_connection_with_processes(event_dict, processes)
128
177
 
178
+ return event_dict
129
179
 
130
- def stop_session_by_name(session_name: str):
131
- """
132
- Stop ETW session by name.
180
+ def _get_processes_from_poller(self):
181
+ processes: dict = {}
182
+ while not processes:
183
+ processes = self.process_poller.get_processes()
133
184
 
134
- :param session_name: The name of the session to stop.
135
- """
185
+ if isinstance(processes, BaseException):
186
+ raise processes
136
187
 
137
- return
188
+ return processes
@@ -19,20 +19,15 @@ WAIT_FOR_PROCESS_POLLER_PID_COUNTS: int = WAIT_FOR_PROCESS_POLLER_PID_SECONDS *
19
19
 
20
20
 
21
21
  class DnsRequestResponseTrace:
22
+ """DnsTrace class use to trace DNS events from Windows Event Tracing for EventId 3008."""
22
23
  def __init__(
23
24
  self,
24
25
  attrs: list = None,
25
26
  session_name: str = None,
26
27
  close_existing_session_name: bool = True,
27
- enable_process_poller: bool = False,
28
28
  process_poller_etw_session_name: str = None
29
29
  ):
30
30
  """
31
- DnsTrace class use to trace DNS events from Windows Event Tracing for EventId 3008.
32
-
33
- :param enable_process_poller: Boolean to enable process poller. Gets the process PID, Name and CommandLine,
34
- every 100 ms. Since the DNS events doesn't contain the process name and command line, only PID.
35
- Then DNS events will be enriched with the process name and command line from the process poller.
36
31
  :param attrs: List of attributes to return. If None, all attributes will be returned.
37
32
  :param session_name: The name of the session to create. If not provided, a UUID will be generated.
38
33
  :param close_existing_session_name: Boolean to close existing session names.
@@ -63,7 +58,6 @@ class DnsRequestResponseTrace:
63
58
  dns_trace_w.stop()
64
59
  """
65
60
 
66
- self.enable_process_poller = enable_process_poller
67
61
  self.attrs = attrs
68
62
 
69
63
  if not session_name:
@@ -74,26 +68,17 @@ class DnsRequestResponseTrace:
74
68
  # lambda x: self.event_queue.put(x),
75
69
  event_id_filters=[REQUEST_RESP_EVENT_ID],
76
70
  session_name=session_name,
77
- close_existing_session_name=close_existing_session_name
71
+ close_existing_session_name=close_existing_session_name,
72
+ enable_process_poller=True,
73
+ process_poller_etw_session_name=process_poller_etw_session_name
78
74
  )
79
75
 
80
- if self.enable_process_poller:
81
- self.process_poller = ProcessPollerPool(
82
- operation='process', poller_method='sysmon_etw',
83
- sysmon_etw_session_name=process_poller_etw_session_name)
84
-
85
76
  def start(self):
86
- if self.enable_process_poller:
87
- self.process_poller.start()
88
-
89
77
  self.event_trace.start()
90
78
 
91
79
  def stop(self):
92
80
  self.event_trace.stop()
93
81
 
94
- if self.enable_process_poller:
95
- self.process_poller.stop()
96
-
97
82
  def emit(self):
98
83
  """
99
84
  Function that will return the next event from the queue.
@@ -107,29 +92,26 @@ class DnsRequestResponseTrace:
107
92
  :return: Dictionary with the event data.
108
93
  """
109
94
 
110
- # Get the processes first, since we need the process name and command line.
111
- # If they're not ready, we will get just pids from DNS tracing.
112
- if self.enable_process_poller:
113
- self._get_processes_from_poller()
114
-
115
95
  event = self.event_trace.emit()
116
96
 
117
97
  event_dict: dict = {
118
- 'pid': event[1]['EventHeader']['ProcessId'],
119
- 'etw_id': event[0],
120
- 'domain': event[1]['QueryName'],
121
- 'query_type_id': str(event[1]['QueryType']),
122
- 'query_type': dns.TYPES_DICT[str(event[1]['QueryType'])]
98
+ 'event_id': event['event_id'],
99
+ 'domain': event['event']['QueryName'],
100
+ 'query_type_id': str(event['event']['QueryType']),
101
+ 'query_type': dns.TYPES_DICT[str(event['event']['QueryType'])],
102
+ 'pid': event['pid'],
103
+ 'name': event['name'],
104
+ 'cmdline': event['cmdline']
123
105
  }
124
106
 
125
107
  # Defining list if ips and other answers, which aren't IPs.
126
108
  list_of_ips = list()
127
109
  list_of_other_domains = list()
128
110
  # Parse DNS results, only if 'QueryResults' key isn't empty, since many of the events are, mostly due errors.
129
- if event[1]['QueryResults']:
111
+ if event['event']['QueryResults']:
130
112
  # 'QueryResults' key contains a string with all the 'Answers' divided by type and ';' character.
131
113
  # Basically, we can parse each type out of string, but we need only IPs and other answers.
132
- list_of_parameters = event[1]['QueryResults'].split(';')
114
+ list_of_parameters = event['event']['QueryResults'].split(';')
133
115
 
134
116
  # Iterating through all the parameters that we got from 'QueryResults' key.
135
117
  for parameter in list_of_parameters:
@@ -149,47 +131,17 @@ class DnsRequestResponseTrace:
149
131
  event_dict['other_domains'] = list_of_other_domains
150
132
 
151
133
  # Getting the 'QueryStatus' key.
152
- event_dict['status_id'] = event[1]['QueryStatus']
134
+ event_dict['status_id'] = event['event']['QueryStatus']
153
135
 
154
136
  # Getting the 'QueryStatus' key. If DNS Query Status is '0' then it was executed successfully.
155
137
  # And if not, it means there was an error. The 'QueryStatus' indicate what number of an error it is.
156
- if event[1]['QueryStatus'] == '0':
138
+ if event['event']['QueryStatus'] == '0':
157
139
  event_dict['status'] = 'Success'
158
140
  else:
159
141
  event_dict['status'] = 'Error'
160
142
 
161
- if self.enable_process_poller:
162
- processes = self.process_poller.get_processes()
163
- if event_dict['pid'] not in processes:
164
- counter = 0
165
- while counter < WAIT_FOR_PROCESS_POLLER_PID_COUNTS:
166
- processes = self.process_poller.get_processes()
167
- if event_dict['pid'] not in processes:
168
- time.sleep(0.1)
169
- counter += 1
170
- else:
171
- break
172
-
173
- if counter == WAIT_FOR_PROCESS_POLLER_PID_COUNTS:
174
- print_api(f"Error: Couldn't get the process name for PID: {event_dict['pid']}.", color='red')
175
-
176
- event_dict = psutilw.cross_single_connection_with_processes(event_dict, processes)
177
- # If it was impossible to get the process name from the process poller, get it from psutil.
178
- # if event_dict['name'].isnumeric():
179
- # event_dict['name'] = process_name
180
-
181
143
  if self.attrs:
182
144
  event_dict = dicts.reorder_keys(
183
145
  event_dict, self.attrs, skip_keys_not_in_list=True)
184
146
 
185
147
  return event_dict
186
-
187
- def _get_processes_from_poller(self):
188
- processes: dict = {}
189
- while not processes:
190
- processes = self.process_poller.get_processes()
191
-
192
- if isinstance(processes, BaseException):
193
- raise processes
194
-
195
- return processes
@@ -1,8 +1,6 @@
1
1
  from .. import trace, const
2
- from ...wrappers.psutilw import psutilw
3
2
  from ...wrappers import sysmonw
4
3
  from ...basics import dicts
5
- from ...print_api import print_api
6
4
 
7
5
 
8
6
  PROVIDER_NAME: str = const.ETW_SYSMON['provider_name']
@@ -87,26 +85,26 @@ class SysmonProcessCreationTrace:
87
85
  event = self.event_trace.emit()
88
86
 
89
87
  event_dict: dict = {
90
- 'etw_id': event[0],
91
- 'pid': event[1]['ProcessId'],
92
- 'process_guid': event[1]['ProcessGuid'],
93
- 'image': event[1]['Image'],
94
- 'file_version': event[1]['FileVersion'],
95
- 'product': event[1]['Product'],
96
- 'company': event[1]['Company'],
97
- 'original_file_name': event[1]['OriginalFileName'],
98
- 'command_line': event[1]['CommandLine'],
99
- 'current_directory': event[1]['CurrentDirectory'],
100
- 'user': event[1]['User'],
101
- 'logon_id': event[1]['LogonId'],
102
- 'logon_guid': event[1]['LogonGuid'],
103
- 'terminal_session_id': event[1]['TerminalSessionId'],
104
- 'integrity_level': event[1]['IntegrityLevel'],
105
- 'hashes': event[1]['Hashes'],
106
- 'parent_process_guid': event[1]['ParentProcessGuid'],
107
- 'parent_process_id': event[1]['ParentProcessId'],
108
- 'parent_image': event[1]['ParentImage'],
109
- 'parent_command_line': event[1]['ParentCommandLine']
88
+ 'event_id': event['event_id'],
89
+ 'pid': event['event']['ProcessId'],
90
+ 'process_guid': event['event']['ProcessGuid'],
91
+ 'image': event['event']['Image'],
92
+ 'file_version': event['event']['FileVersion'],
93
+ 'product': event['event']['Product'],
94
+ 'company': event['event']['Company'],
95
+ 'original_file_name': event['event']['OriginalFileName'],
96
+ 'command_line': event['event']['CommandLine'],
97
+ 'current_directory': event['event']['CurrentDirectory'],
98
+ 'user': event['event']['User'],
99
+ 'logon_id': event['event']['LogonId'],
100
+ 'logon_guid': event['event']['LogonGuid'],
101
+ 'terminal_session_id': event['event']['TerminalSessionId'],
102
+ 'integrity_level': event['event']['IntegrityLevel'],
103
+ 'hashes': event['event']['Hashes'],
104
+ 'parent_process_guid': event['event']['ParentProcessGuid'],
105
+ 'parent_process_id': event['event']['ParentProcessId'],
106
+ 'parent_image': event['event']['ParentImage'],
107
+ 'parent_command_line': event['event']['ParentCommandLine']
110
108
  }
111
109
 
112
110
  if self.attrs:
@@ -28,7 +28,6 @@ class DnsCheck:
28
28
  attrs=['name', 'cmdline', 'domain', 'query_type'],
29
29
  session_name=self.etw_session_name,
30
30
  close_existing_session_name=True,
31
- enable_process_poller=True,
32
31
  process_poller_etw_session_name=change_monitor_instance.etw_process_session_name
33
32
  )
34
33
  )
@@ -6,7 +6,7 @@ from typing import Literal, Union
6
6
  from .wrappers.pywin32w import wmi_win32process
7
7
  from .wrappers.psutilw import psutilw
8
8
  from .etws.traces import trace_sysmon_process_creation
9
- from .basics import list_of_dicts, dicts
9
+ from .basics import dicts
10
10
  from .process_name_cmd import ProcessNameCmdline
11
11
  from .print_api import print_api
12
12
 
@@ -322,6 +322,7 @@ def _worker(
322
322
  running_state = False
323
323
  exception = e
324
324
  print_api(f'Exception in ProcessPollerPool: {e}', color='red')
325
+ raise
325
326
 
326
327
  if not running_state:
327
328
  process_queue.put(exception)
File without changes
File without changes
@@ -0,0 +1,149 @@
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
+
8
+
9
+ class EventLogSubscriber:
10
+ """
11
+ Class for subscribing to Windows Event Log events.
12
+
13
+ Usage:
14
+ from atomicshop.wrappers.pywin32w.win_event_log import subscribe
15
+
16
+ event_log_subscriber = subscribe.EventLogSubscriber('Security', 4688)
17
+ event_log_subscriber.start()
18
+
19
+ while True:
20
+ event = event_log_subscriber.emit()
21
+ print(event)
22
+ """
23
+ def __init__(self, log_channel: str, event_id: int):
24
+ """
25
+ :param log_channel: The name of the event log channel to subscribe to. Examples:
26
+ Security, System, Application, etc.
27
+ :param event_id: The ID of the event to subscribe to.
28
+ Example: 4688 for process creation events in "Security" channel.
29
+ """
30
+ self.log_channel: str = log_channel
31
+ self.event_id: str = str(event_id)
32
+
33
+ self._event_queue = queue.Queue()
34
+ self._subscription_thread = None
35
+
36
+ def start(self):
37
+ """Start the subscription process."""
38
+ self._subscription_thread = threading.Thread(
39
+ target=start_subscription, args=(self.log_channel, self.event_id, self._event_queue)
40
+ )
41
+ self._subscription_thread.daemon = True
42
+ self._subscription_thread.start()
43
+
44
+ def stop(self):
45
+ """Stop the subscription process."""
46
+ if self._subscription_thread:
47
+ self._subscription_thread.join()
48
+ self._subscription_thread = None
49
+
50
+ def emit(self, timeout: float = None) -> Union[dict, None]:
51
+ """
52
+ Get the next event from the event queue.
53
+
54
+ :param timeout: The maximum time (in seconds) to wait for an event.
55
+ If None, the function will block until an event is available.
56
+ :return: A dictionary containing the event data, or None if no event is available.
57
+ """
58
+ try:
59
+ return self._event_queue.get(timeout=timeout)
60
+ except queue.Empty:
61
+ return None
62
+
63
+
64
+ def _parse_event_xml(event_xml):
65
+ root = Et.fromstring(event_xml)
66
+ data = {}
67
+ for elem in root.iter():
68
+ if 'Name' in elem.attrib:
69
+ data[elem.attrib['Name']] = elem.text
70
+ return data
71
+
72
+
73
+ def _handle_event(event, event_queue):
74
+ event_xml = win32evtlog.EvtRender(event, win32evtlog.EvtRenderEventXml)
75
+ try:
76
+ data = _parse_event_xml(event_xml)
77
+ except Et.ParseError as e:
78
+ 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"
96
+
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)
112
+
113
+
114
+ def _event_callback(action, context, event):
115
+ event_queue = context['event_queue']
116
+ if action == win32evtlog.EvtSubscribeActionDeliver:
117
+ _handle_event(event, event_queue)
118
+
119
+
120
+ def start_subscription(log_channel: str, event_id: int, event_queue):
121
+ """
122
+ Start listening for events in the specified log channel with the given event ID.
123
+
124
+ :param log_channel: The name of the event log channel to subscribe to. Examples:
125
+ Security, System, Application, etc.
126
+ :param event_id: The ID of the event to subscribe to.
127
+ Example: 4688 for process creation events in "Security" channel.
128
+ :param event_queue: A queue to store the received events
129
+ """
130
+ # This selects the System node within each event.
131
+ # 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)}]"
133
+
134
+ subscription = win32evtlog.EvtSubscribe(
135
+ log_channel,
136
+ win32evtlog.EvtSubscribeToFutureEvents,
137
+ SignalEvent=None,
138
+ Query=query,
139
+ Callback=_event_callback,
140
+ Context={'event_queue': event_queue}
141
+ )
142
+
143
+ print("Listening for new process creation events...")
144
+
145
+ try:
146
+ while True:
147
+ time.sleep(1)
148
+ except KeyboardInterrupt:
149
+ print("Stopped listening for events.")
@@ -0,0 +1,145 @@
1
+ import subprocess
2
+ import winreg
3
+
4
+ from .. import subscribe
5
+ from .....print_api import print_api
6
+
7
+
8
+ AUDITING_REG_PATH: str = r"Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit"
9
+ PROCESS_CREATION_INCLUDE_CMDLINE_VALUE: str = "ProcessCreationIncludeCmdLine_Enabled"
10
+
11
+
12
+ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
13
+ """
14
+ Class for subscribing to Windows Event Log events related to process creation.
15
+
16
+ Usage:
17
+ from atomicshop.wrappers.pywin32w.win_event_log.subscribes import subscribe_to_process_create
18
+
19
+ process_create_subscriber = subscribe_to_process_create.ProcessCreateSubscriber()
20
+ process_create_subscriber.start()
21
+
22
+ while True:
23
+ event = process_create_subscriber.emit()
24
+ print(event)
25
+ """
26
+ def __init__(self):
27
+ super().__init__('Security', 4688)
28
+
29
+ def start(self):
30
+ """Start the subscription process."""
31
+ enable_audit_process_creation()
32
+ enable_command_line_auditing()
33
+
34
+ super().start()
35
+
36
+ def stop(self):
37
+ """Stop the subscription process."""
38
+ super().stop()
39
+
40
+ def emit(self, timeout: float = None) -> dict:
41
+ """
42
+ Get the next event from the event queue.
43
+
44
+ :param timeout: The maximum time (in seconds) to wait for an event.
45
+ If None, the function will block until an event is available.
46
+ :return: A dictionary containing the event data.
47
+ """
48
+ return super().emit(timeout=timeout)
49
+
50
+
51
+ def enable_audit_process_creation(print_kwargs: dict = None):
52
+ """
53
+ Enable the 'Audit Process Creation' policy.
54
+
55
+ :param print_kwargs: Optional keyword arguments for the print function.
56
+ """
57
+ if is_audit_process_creation_enabled():
58
+ print_api("Audit Process Creation is already enabled.", color='yellow', **(print_kwargs or {}))
59
+ return
60
+
61
+ # Enable "Audit Process Creation" policy
62
+ audit_policy_command = [
63
+ "auditpol", "/set", "/subcategory:Process Creation", "/success:enable", "/failure:enable"
64
+ ]
65
+ try:
66
+ subprocess.run(audit_policy_command, check=True)
67
+ print_api("Successfully enabled 'Audit Process Creation'.", color='green', **(print_kwargs or {}))
68
+ except subprocess.CalledProcessError as e:
69
+ print_api(f"Failed to enable 'Audit Process Creation': {e}", error_type=True, color='red', **(print_kwargs or {}))
70
+ raise e
71
+
72
+
73
+ def is_audit_process_creation_enabled(print_kwargs: dict = None) -> bool:
74
+ """
75
+ Check if the 'Audit Process Creation' policy is enabled.
76
+
77
+ :param print_kwargs: Optional keyword arguments for the print function.
78
+ """
79
+ # Command to check the "Audit Process Creation" policy
80
+ audit_policy_check_command = [
81
+ "auditpol", "/get", "/subcategory:Process Creation"
82
+ ]
83
+ try:
84
+ result = subprocess.run(audit_policy_check_command, check=True, capture_output=True, text=True)
85
+ output = result.stdout
86
+ # print_api(output) # Print the output for inspection
87
+
88
+ if "Process Creation" in output and "Success and Failure" in output:
89
+ # print_api(
90
+ # "'Audit Process Creation' is enabled for both success and failure.",
91
+ # color='green', **(print_kwargs or {}))
92
+ return True
93
+ 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 {}))
98
+ return False
99
+ except subprocess.CalledProcessError as e:
100
+ print_api(f"Failed to check 'Audit Process Creation': {e}", color='red', error_type=True, **(print_kwargs or {}))
101
+ return False
102
+
103
+
104
+ def enable_command_line_auditing(print_kwargs: dict = None):
105
+ """
106
+ Enable the 'Include command line in process creation events' policy.
107
+
108
+ :param print_kwargs: Optional keyword arguments for the print function.
109
+ """
110
+
111
+ if is_command_line_auditing_enabled():
112
+ print_api(
113
+ "'Include command line in process creation events' is already enabled.", color='yellow',
114
+ **(print_kwargs or {}))
115
+ return
116
+
117
+ try:
118
+ # Open the registry key
119
+ with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH) as reg_key:
120
+ # Set the value
121
+ winreg.SetValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE, 0, winreg.REG_DWORD, 1)
122
+
123
+ print_api(
124
+ "Successfully enabled 'Include command line in process creation events'.",
125
+ color='green', **(print_kwargs or {}))
126
+ except WindowsError as e:
127
+ print_api(
128
+ f"Failed to enable 'Include command line in process creation events': {e}", error_type=True,
129
+ color='red', **(print_kwargs or {}))
130
+
131
+
132
+ def is_command_line_auditing_enabled():
133
+ try:
134
+ # Open the registry key
135
+ with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH, 0, winreg.KEY_READ) as reg_key:
136
+ # Query the value
137
+ value, reg_type = winreg.QueryValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE)
138
+ # Check if the value is 1 (enabled)
139
+ return value == 1
140
+ except FileNotFoundError:
141
+ # Key or value not found, assume it's not enabled
142
+ return False
143
+ except WindowsError as e:
144
+ print(f"Failed to read the 'Include command line in process creation events' setting: {e}")
145
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.13.1
3
+ Version: 2.14.0
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=aqwPLStJUJ8YnGq3I420Yccvy8q9JIXVsivcavrcOtM,123
1
+ atomicshop/__init__.py,sha256=LlDA7S0dPFwG14ZFuGv5tyUVyow2__rgMRanlcYHT88,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=sEuuFu3gmUrp4tY1LVxE27L0eK5LIwNKjTbV7KtzeRM,15029
30
+ atomicshop/process_poller.py,sha256=sGIiLNdYdyxdER7hKP8QcbYg1esKe5ymCfh5qUATpJM,15033
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,10 +106,10 @@ 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=tjBvV4RxCSn8Aoxj8NVxmqHekdECne_y4Zv2lbh11ak,5341
109
+ atomicshop/etws/trace.py,sha256=H0uDB6I86UoNyijE5HCukp_paJNHTjKL6eAzBRGSI50,8509
110
110
  atomicshop/etws/traces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
- atomicshop/etws/traces/trace_dns.py,sha256=wRFwfFgVgMbYJu1Sf0Yy9LPoMrf9kXejl2Xjk3PNELQ,8430
112
- atomicshop/etws/traces/trace_sysmon_process_creation.py,sha256=pjb0qzNZttucEWOKv2j_BGVqaSeKjBL4O8oCsAteiGU,4785
111
+ atomicshop/etws/traces/trace_dns.py,sha256=8Fl5UW4An3SE8tGXGTufTgOcWa574YZ6zQbU7x5DYrs,6270
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
115
115
  atomicshop/file_io/docxs.py,sha256=6tcYFGp0vRsHR47VwcRqwhdt2DQOwrAUYhrwN996n9U,5117
@@ -141,7 +141,7 @@ atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha
141
141
  atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
142
  atomicshop/monitor/change_monitor.py,sha256=eJRP7NBnA-Y_n6Ofm_jKrR5XguJV6gEmxFdtdGMBF3k,7866
143
143
  atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
- atomicshop/monitor/checks/dns.py,sha256=_DxInUSiiMa8Bx1sYJ6fRzRl7n_eAsoaGHhUk9XgF7w,7182
144
+ atomicshop/monitor/checks/dns.py,sha256=OJWvMKl0zNtvBTYVYHSK_tfH0cNxCJ1kc_O-2Ob_5fc,7137
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
@@ -246,9 +246,14 @@ atomicshop/wrappers/psutilw/disks.py,sha256=3ZSVoommKH1TWo37j_83frB-NqXF4Nf5q5mB
246
246
  atomicshop/wrappers/psutilw/memories.py,sha256=_S0aL8iaoIHebd1vOFrY_T9aROM5Jx2D5CvDh_4j0Vc,528
247
247
  atomicshop/wrappers/psutilw/psutilw.py,sha256=q3EwgprqyrR4zLCjl4l5DHFOQoukEvQMIPjNB504oQ0,21262
248
248
  atomicshop/wrappers/psycopgw/psycopgw.py,sha256=XJvVf0oAUjCHkrYfKeFuGCpfn0Oxj3u4SbKMKA1508E,7118
249
+ atomicshop/wrappers/pywin32w/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
250
  atomicshop/wrappers/pywin32w/console.py,sha256=LstHajPLgXp9qQxFNR44QfH10nOnNp3bCJquxaTquns,1175
250
251
  atomicshop/wrappers/pywin32w/winshell.py,sha256=i2bKiMldPU7_azsD5xGQDdMwjaM7suKJd3k0Szmcs6c,723
251
252
  atomicshop/wrappers/pywin32w/wmi_win32process.py,sha256=qMzXtJ5hBZ5ydAyqpDbSx0nO2RJQL95HdmV5SzNKMhk,6826
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
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
252
257
  atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
258
  atomicshop/wrappers/socketw/accepter.py,sha256=HQC1EyZmyUtVEfFbaBkHCE-VZp6RWyd9mEqAkgsE1fk,1749
254
259
  atomicshop/wrappers/socketw/base.py,sha256=1vvg8EhRGvnxdrRAm1VJSLCXkm2SZDHRjdpTuhkH3Mg,1844
@@ -265,8 +270,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
265
270
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
266
271
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
267
272
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
268
- atomicshop-2.13.1.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
269
- atomicshop-2.13.1.dist-info/METADATA,sha256=yOVvRROyXl4BM9Qr9EzI5BTZgvEo0OG3-uCo9Ryni2Y,10478
270
- atomicshop-2.13.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
271
- atomicshop-2.13.1.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
272
- atomicshop-2.13.1.dist-info/RECORD,,
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,,