atomicshop 2.13.2__py3-none-any.whl → 2.14.1__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 +1 -1
- atomicshop/etws/trace.py +12 -5
- atomicshop/etws/traces/trace_dns.py +3 -7
- atomicshop/monitor/change_monitor.py +1 -5
- atomicshop/monitor/checks/dns.py +1 -2
- atomicshop/process_poller.py +20 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +149 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/subscribe_to_process_create.py +145 -0
- {atomicshop-2.13.2.dist-info → atomicshop-2.14.1.dist-info}/METADATA +1 -1
- {atomicshop-2.13.2.dist-info → atomicshop-2.14.1.dist-info}/RECORD +16 -11
- {atomicshop-2.13.2.dist-info → atomicshop-2.14.1.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.13.2.dist-info → atomicshop-2.14.1.dist-info}/WHEEL +0 -0
- {atomicshop-2.13.2.dist-info → atomicshop-2.14.1.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/etws/trace.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import queue
|
|
2
2
|
import sys
|
|
3
3
|
import time
|
|
4
|
+
from typing import Literal
|
|
4
5
|
|
|
5
6
|
# Import FireEye Event Tracing library.
|
|
6
7
|
import etw
|
|
@@ -26,7 +27,7 @@ class EventTrace(etw.ETW):
|
|
|
26
27
|
session_name: str = None,
|
|
27
28
|
close_existing_session_name: bool = True,
|
|
28
29
|
enable_process_poller: bool = False,
|
|
29
|
-
|
|
30
|
+
process_poller_method: Literal['psutil', 'pywin32', 'process_dll', 'sysmon_etw', 'event_log'] = 'event_log'
|
|
30
31
|
):
|
|
31
32
|
"""
|
|
32
33
|
:param providers: List of tuples with provider name and provider GUID.
|
|
@@ -40,7 +41,13 @@ class EventTrace(etw.ETW):
|
|
|
40
41
|
:param enable_process_poller: Boolean to enable process poller. Gets the process PID, Name and CommandLine.
|
|
41
42
|
Since the DNS events doesn't contain the process name and command line, only PID.
|
|
42
43
|
Then DNS events will be enriched with the process name and command line from the process poller.
|
|
43
|
-
:param
|
|
44
|
+
:param process_poller_method: The method to get the process information. For more information, see the
|
|
45
|
+
'process_poller.ProcessPollerPool' class. Summary:
|
|
46
|
+
'psutil': Uses 'psutil' library to get the process information.
|
|
47
|
+
'pywin32': Uses 'pywin32' library to get the process information.
|
|
48
|
+
'process_dll': Uses 'process' custom DLL to get the process information.
|
|
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.
|
|
44
51
|
|
|
45
52
|
------------------------------------------
|
|
46
53
|
|
|
@@ -64,7 +71,6 @@ class EventTrace(etw.ETW):
|
|
|
64
71
|
self.event_queue = queue.Queue()
|
|
65
72
|
self.close_existing_session_name: bool = close_existing_session_name
|
|
66
73
|
self.enable_process_poller: bool = enable_process_poller
|
|
67
|
-
self.process_poller_etw_session_name: str = process_poller_etw_session_name
|
|
68
74
|
|
|
69
75
|
# If no callback function is provided, we will use the default one, which will put the event in the queue.
|
|
70
76
|
if not event_callback:
|
|
@@ -78,12 +84,13 @@ class EventTrace(etw.ETW):
|
|
|
78
84
|
for provider in providers:
|
|
79
85
|
etw_format_providers.append(etw.ProviderInfo(provider[0], etw.GUID(provider[1])))
|
|
80
86
|
|
|
81
|
-
|
|
87
|
+
process_poller_etw_session_name = None
|
|
88
|
+
if process_poller_method == 'sysmon_etw':
|
|
82
89
|
process_poller_etw_session_name = PROCESS_POLLER_ETW_DEFAULT_SESSION_NAME
|
|
83
90
|
|
|
84
91
|
if self.enable_process_poller:
|
|
85
92
|
self.process_poller = process_poller.ProcessPollerPool(
|
|
86
|
-
operation='process', poller_method=
|
|
93
|
+
operation='process', poller_method=process_poller_method,
|
|
87
94
|
sysmon_etw_session_name=process_poller_etw_session_name)
|
|
88
95
|
|
|
89
96
|
super().__init__(
|
|
@@ -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
|
|
atomicshop/monitor/checks/dns.py
CHANGED
|
@@ -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
|
|
atomicshop/process_poller.py
CHANGED
|
@@ -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 subscribe_to_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'
|
|
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'] = '
|
|
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 = subscribe_to_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:
|
|
@@ -289,6 +300,13 @@ def _worker(
|
|
|
289
300
|
'name': current_cycle['original_file_name'],
|
|
290
301
|
'cmdline': current_cycle['command_line']}
|
|
291
302
|
}
|
|
303
|
+
elif poller_method == 'event_log':
|
|
304
|
+
# Get the current processes and reinitialize the instance of the dict.
|
|
305
|
+
current_cycle: dict = poller_instance.emit()
|
|
306
|
+
current_processes: dict = {current_cycle['pid']: {
|
|
307
|
+
'name': Path(current_cycle['process_name']).name,
|
|
308
|
+
'cmdline': current_cycle['command_line']}
|
|
309
|
+
}
|
|
292
310
|
else:
|
|
293
311
|
# Get the current processes and reinitialize the instance of the dict.
|
|
294
312
|
current_processes: dict = dict(poller_instance.get_processes())
|
|
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.")
|
|
File without changes
|
|
@@ -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,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=w-I1vM1vpc9PJDvoRTt804j2SwPM8mjp6n5FOtCdutg,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=
|
|
30
|
+
atomicshop/process_poller.py,sha256=B91ugFIo84IMFXbWJeW8P7TsIkzYxMXBVWuvYqRXS-c,16107
|
|
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=
|
|
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
|
|
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=
|
|
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=
|
|
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
|
|
@@ -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=Cviy1n3NSCGswQ4TxAyeDh7d21u8Vt3UOjnLmYYU2fQ,5602
|
|
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.
|
|
269
|
-
atomicshop-2.
|
|
270
|
-
atomicshop-2.
|
|
271
|
-
atomicshop-2.
|
|
272
|
-
atomicshop-2.
|
|
273
|
+
atomicshop-2.14.1.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
274
|
+
atomicshop-2.14.1.dist-info/METADATA,sha256=rZNM503plkk4O9jvmeQTuAHA-7JS24mxM3k7bHNo0SA,10478
|
|
275
|
+
atomicshop-2.14.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
276
|
+
atomicshop-2.14.1.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
277
|
+
atomicshop-2.14.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|