atomicshop 2.19.6__py3-none-any.whl → 2.19.7__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/basics/bytes_arrays.py +19 -0
- atomicshop/etws/trace.py +2 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
- {atomicshop-2.19.6.dist-info → atomicshop-2.19.7.dist-info}/METADATA +1 -1
- {atomicshop-2.19.6.dist-info → atomicshop-2.19.7.dist-info}/RECORD +10 -10
- {atomicshop-2.19.6.dist-info → atomicshop-2.19.7.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.19.6.dist-info → atomicshop-2.19.7.dist-info}/WHEEL +0 -0
- {atomicshop-2.19.6.dist-info → atomicshop-2.19.7.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import string
|
|
3
|
+
|
|
4
|
+
|
|
1
5
|
def get_single_byte_from_byte_string(byte_string, index: int):
|
|
2
6
|
"""
|
|
3
7
|
Function extracts single byte as byte from byte string object.
|
|
@@ -179,3 +183,18 @@ def read_bytes_from_position(
|
|
|
179
183
|
# Read the specified number of bytes.
|
|
180
184
|
data = file.read(num_bytes)
|
|
181
185
|
return data
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def convert_bytes_to_printable_string_only(
|
|
189
|
+
byte_sequence: Union[bytes, bytearray],
|
|
190
|
+
non_printable_character: str = '.'
|
|
191
|
+
) -> str:
|
|
192
|
+
"""
|
|
193
|
+
Convert bytes to printable string. If byte is not printable, replace it with 'non_printable_character'.
|
|
194
|
+
:param byte_sequence: bytes or bytearray, sequence of bytes.
|
|
195
|
+
:param non_printable_character: string, character to replace non-printable characters.
|
|
196
|
+
:return:
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
printable = set(string.printable)
|
|
200
|
+
return ''.join(chr(byte) if chr(byte) in printable else non_printable_character for byte in byte_sequence)
|
atomicshop/etws/trace.py
CHANGED
|
@@ -29,6 +29,8 @@ class EventTrace(etw.ETW):
|
|
|
29
29
|
:param providers: List of tuples with provider name and provider GUID.
|
|
30
30
|
tuple[0] = provider name
|
|
31
31
|
tuple[1] = provider GUID
|
|
32
|
+
|
|
33
|
+
Example: [('Microsoft-Windows-DNS-Client', '{1c95126e-7ee8-4e23-86b2-6e7e4a5a8e9b}')]
|
|
32
34
|
:param event_callback: Reference to the callable callback function that will be called for each occurring event.
|
|
33
35
|
:param event_id_filters: List of event IDs that we want to filter. If not provided, all events will be returned.
|
|
34
36
|
The default in the 'etw.ETW' method is 'None'.
|
|
@@ -11,10 +11,14 @@ EVENT_CONTROL_CODE_ENABLE_PROVIDER = 1
|
|
|
11
11
|
|
|
12
12
|
MAXIMUM_LOGGERS = 64
|
|
13
13
|
ULONG64 = ctypes.c_uint64
|
|
14
|
+
UCHAR = ctypes.c_ubyte
|
|
14
15
|
|
|
15
16
|
INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
|
|
16
17
|
TRACEHANDLE = ULONG64
|
|
17
18
|
|
|
19
|
+
PROCESS_TRACE_MODE_EVENT_RECORD = 0x10000000 # new event-record callback
|
|
20
|
+
PROCESS_TRACE_MODE_REAL_TIME = 0x00000100
|
|
21
|
+
INVALID_PROCESSTRACE_HANDLE = 0xFFFFFFFFFFFFFFFF # Often -1 in 64-bit
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
"""
|
|
@@ -69,10 +73,7 @@ class EVENT_TRACE_PROPERTIES(ctypes.Structure):
|
|
|
69
73
|
("RealTimeBuffersLost", wintypes.ULONG),
|
|
70
74
|
("LoggerThreadId", wintypes.HANDLE),
|
|
71
75
|
("LogFileNameOffset", wintypes.ULONG),
|
|
72
|
-
("LoggerNameOffset", wintypes.ULONG)
|
|
73
|
-
# Allocate space for the names at the end of the structure
|
|
74
|
-
("_LoggerName", wintypes.WCHAR * 1024),
|
|
75
|
-
("_LogFileName", wintypes.WCHAR * 1024)
|
|
76
|
+
("LoggerNameOffset", wintypes.ULONG)
|
|
76
77
|
]
|
|
77
78
|
|
|
78
79
|
|
|
@@ -183,55 +184,59 @@ class EVENT_HEADER(ctypes.Structure):
|
|
|
183
184
|
("RelatedActivityId", GUID),
|
|
184
185
|
]
|
|
185
186
|
|
|
187
|
+
|
|
188
|
+
class ETW_BUFFER_CONTEXT(ctypes.Structure):
|
|
189
|
+
_fields_ = [('ProcessorNumber', ctypes.c_ubyte),
|
|
190
|
+
('Alignment', ctypes.c_ubyte),
|
|
191
|
+
('LoggerId', ctypes.c_ushort)]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class EVENT_HEADER_EXTENDED_DATA_ITEM(ctypes.Structure):
|
|
195
|
+
_fields_ = [
|
|
196
|
+
('Reserved1', ctypes.c_ushort),
|
|
197
|
+
('ExtType', ctypes.c_ushort),
|
|
198
|
+
('Linkage', ctypes.c_ushort), # struct{USHORT :1, USHORT :15}
|
|
199
|
+
('DataSize', ctypes.c_ushort),
|
|
200
|
+
('DataPtr', ctypes.c_ulonglong)
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
|
|
186
204
|
class EVENT_RECORD(ctypes.Structure):
|
|
187
205
|
_fields_ = [
|
|
188
|
-
(
|
|
189
|
-
(
|
|
190
|
-
(
|
|
191
|
-
(
|
|
192
|
-
(
|
|
193
|
-
(
|
|
194
|
-
(
|
|
206
|
+
('EventHeader', EVENT_HEADER),
|
|
207
|
+
('BufferContext', ETW_BUFFER_CONTEXT),
|
|
208
|
+
('ExtendedDataCount', ctypes.c_ushort),
|
|
209
|
+
('UserDataLength', ctypes.c_ushort),
|
|
210
|
+
('ExtendedData', ctypes.POINTER(EVENT_HEADER_EXTENDED_DATA_ITEM)),
|
|
211
|
+
('UserData', ctypes.c_void_p),
|
|
212
|
+
('UserContext', ctypes.c_void_p)
|
|
195
213
|
]
|
|
196
214
|
|
|
197
215
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
# ("ProcessTraceMode", wintypes.ULONG),
|
|
205
|
-
# ("EventRecordCallback", wintypes.LPVOID),
|
|
206
|
-
# ("BufferSize", wintypes.ULONG),
|
|
207
|
-
# ("Filled", wintypes.ULONG),
|
|
208
|
-
# ("EventsLost", wintypes.ULONG),
|
|
209
|
-
# ("BuffersLost", wintypes.ULONG),
|
|
210
|
-
# ("RealTimeBuffersLost", wintypes.ULONG),
|
|
211
|
-
# ("LogBuffersLost", wintypes.ULONG),
|
|
212
|
-
# ("BuffersWritten", wintypes.ULONG),
|
|
213
|
-
# ("LogFileMode", wintypes.ULONG),
|
|
214
|
-
# ("IsKernelTrace", wintypes.ULONG),
|
|
215
|
-
# ("Context", wintypes.ULONG) # Placeholder for context pointer
|
|
216
|
-
# ]
|
|
216
|
+
class EVENT_TRACE_LOGFILE(ctypes.Structure):
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
EVENT_RECORD_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.POINTER(EVENT_RECORD))
|
|
221
|
+
EVENT_TRACE_BUFFER_CALLBACK = ctypes.WINFUNCTYPE(ctypes.c_ulong, ctypes.POINTER(EVENT_TRACE_LOGFILE))
|
|
217
222
|
|
|
218
223
|
|
|
219
224
|
class EVENT_TRACE_LOGFILE(ctypes.Structure):
|
|
220
225
|
_fields_ = [
|
|
221
|
-
(
|
|
222
|
-
(
|
|
223
|
-
(
|
|
224
|
-
(
|
|
225
|
-
(
|
|
226
|
-
(
|
|
227
|
-
(
|
|
228
|
-
(
|
|
229
|
-
(
|
|
230
|
-
(
|
|
231
|
-
(
|
|
232
|
-
(
|
|
233
|
-
(
|
|
234
|
-
(
|
|
226
|
+
('LogFileName', ctypes.c_wchar_p),
|
|
227
|
+
('LoggerName', ctypes.c_wchar_p),
|
|
228
|
+
('CurrentTime', ctypes.c_longlong),
|
|
229
|
+
('BuffersRead', ctypes.c_ulong),
|
|
230
|
+
('ProcessTraceMode', ctypes.c_ulong),
|
|
231
|
+
('CurrentEvent', EVENT_TRACE),
|
|
232
|
+
('LogfileHeader', TRACE_LOGFILE_HEADER),
|
|
233
|
+
('BufferCallback', EVENT_TRACE_BUFFER_CALLBACK),
|
|
234
|
+
('BufferSize', ctypes.c_ulong),
|
|
235
|
+
('Filled', ctypes.c_ulong),
|
|
236
|
+
('EventsLost', ctypes.c_ulong),
|
|
237
|
+
('EventRecordCallback', EVENT_RECORD_CALLBACK),
|
|
238
|
+
('IsKernelTrace', ctypes.c_ulong),
|
|
239
|
+
('Context', ctypes.c_void_p)
|
|
235
240
|
]
|
|
236
241
|
|
|
237
242
|
|
|
@@ -255,7 +260,7 @@ class PROVIDER_INFORMATION(ctypes.Structure):
|
|
|
255
260
|
|
|
256
261
|
|
|
257
262
|
# Load the necessary library
|
|
258
|
-
advapi32 = ctypes.WinDLL(
|
|
263
|
+
advapi32 = ctypes.WinDLL("advapi32", use_last_error=True)
|
|
259
264
|
tdh = ctypes.windll.tdh
|
|
260
265
|
|
|
261
266
|
# Define necessary TDH functions
|
|
@@ -263,6 +268,46 @@ tdh.TdhEnumerateProviders.argtypes = [ctypes.POINTER(PROVIDER_ENUMERATION_INFO),
|
|
|
263
268
|
tdh.TdhEnumerateProviders.restype = ULONG
|
|
264
269
|
|
|
265
270
|
|
|
271
|
+
# Make sure StartTraceW has proper argtypes (if not set in consts)
|
|
272
|
+
StartTrace = advapi32.StartTraceW
|
|
273
|
+
StartTrace.argtypes = [
|
|
274
|
+
ctypes.POINTER(TRACEHANDLE),
|
|
275
|
+
wintypes.LPCWSTR,
|
|
276
|
+
ctypes.POINTER(EVENT_TRACE_PROPERTIES)
|
|
277
|
+
]
|
|
278
|
+
StartTrace.restype = wintypes.ULONG
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class EVENT_FILTER_DESCRIPTOR(ctypes.Structure):
|
|
282
|
+
_fields_ = [('Ptr', ctypes.c_ulonglong),
|
|
283
|
+
('Size', ctypes.c_ulong),
|
|
284
|
+
('Type', ctypes.c_ulong)]
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class ENABLE_TRACE_PARAMETERS(ctypes.Structure):
|
|
288
|
+
_fields_ = [
|
|
289
|
+
('Version', ctypes.c_ulong),
|
|
290
|
+
('EnableProperty', ctypes.c_ulong),
|
|
291
|
+
('ControlFlags', ctypes.c_ulong),
|
|
292
|
+
('SourceId', GUID),
|
|
293
|
+
('EnableFilterDesc', ctypes.POINTER(EVENT_FILTER_DESCRIPTOR)),
|
|
294
|
+
('FilterDescCount', ctypes.c_ulong)
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
EnableTraceEx2 = advapi32.EnableTraceEx2
|
|
299
|
+
EnableTraceEx2.argtypes = [
|
|
300
|
+
TRACEHANDLE, # TraceHandle (c_uint64)
|
|
301
|
+
ctypes.POINTER(GUID), # ProviderId
|
|
302
|
+
ctypes.c_ulong, # ControlCode
|
|
303
|
+
ctypes.c_char, # Level
|
|
304
|
+
ctypes.c_ulonglong, # MatchAnyKeyword
|
|
305
|
+
ctypes.c_ulonglong, # MatchAllKeyword
|
|
306
|
+
ctypes.c_ulong, # Timeout
|
|
307
|
+
ctypes.POINTER(ENABLE_TRACE_PARAMETERS)] # PENABLE_TRACE_PARAMETERS (optional) -> None or pointer
|
|
308
|
+
EnableTraceEx2.restype = ctypes.c_ulong
|
|
309
|
+
|
|
310
|
+
|
|
266
311
|
# Define the function prototype
|
|
267
312
|
QueryAllTraces = advapi32.QueryAllTracesW
|
|
268
313
|
QueryAllTraces.argtypes = [
|
|
@@ -277,8 +322,13 @@ OpenTrace.argtypes = [ctypes.POINTER(EVENT_TRACE_LOGFILE)]
|
|
|
277
322
|
OpenTrace.restype = wintypes.ULONG
|
|
278
323
|
|
|
279
324
|
ProcessTrace = advapi32.ProcessTrace
|
|
280
|
-
ProcessTrace.argtypes = [
|
|
281
|
-
|
|
325
|
+
ProcessTrace.argtypes = [
|
|
326
|
+
ctypes.POINTER(ctypes.c_uint64), # pointer to array of 64-bit handles
|
|
327
|
+
wintypes.ULONG, # handle count
|
|
328
|
+
ctypes.c_void_p, # LPFILETIME (start)
|
|
329
|
+
ctypes.c_void_p # LPFILETIME (end)
|
|
330
|
+
]
|
|
331
|
+
ProcessTrace.restype = wintypes.ULONG
|
|
282
332
|
|
|
283
333
|
CloseTrace = advapi32.CloseTrace
|
|
284
334
|
CloseTrace.argtypes = [wintypes.ULONG]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ctypes
|
|
2
|
-
|
|
2
|
+
import queue
|
|
3
3
|
from ctypes.wintypes import ULONG
|
|
4
4
|
import uuid
|
|
5
5
|
from typing import Literal
|
|
@@ -34,7 +34,7 @@ def start_etw_session(
|
|
|
34
34
|
provider_name_list: list = None,
|
|
35
35
|
verbosity_mode: int = 4,
|
|
36
36
|
maximum_buffers: int = 38
|
|
37
|
-
):
|
|
37
|
+
) -> const.TRACEHANDLE:
|
|
38
38
|
"""
|
|
39
39
|
Start an ETW session and enable the specified provider.
|
|
40
40
|
|
|
@@ -77,57 +77,89 @@ def start_etw_session(
|
|
|
77
77
|
for provider_name in provider_name_list:
|
|
78
78
|
provider_guid_list.append(providers.get_provider_guid_by_name(provider_name))
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
80
|
+
# (1) Allocate a buffer large enough for EVENT_TRACE_PROPERTIES + space for 2 strings
|
|
81
|
+
# The typical approach is to allow enough space for:
|
|
82
|
+
# - The structure
|
|
83
|
+
# - The "LoggerName" string
|
|
84
|
+
# - The "LogFileName" string (even if we don't use it, we must leave offset space)
|
|
85
|
+
#
|
|
86
|
+
props_size = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
|
|
87
|
+
# Add space for 2 x 1024 wide-chars (one for LoggerName, one for LogFileName)
|
|
88
|
+
# Each wide char = ctypes.sizeof(ctypes.c_wchar).
|
|
89
|
+
props_size += (2 * 1024 * ctypes.sizeof(ctypes.c_wchar)) # for logger name
|
|
90
|
+
props_size += (2 * 1024 * ctypes.sizeof(ctypes.c_wchar)) # for log file name
|
|
91
|
+
|
|
92
|
+
properties_buffer = ctypes.create_string_buffer(props_size)
|
|
93
|
+
properties_ptr = ctypes.cast(properties_buffer, ctypes.POINTER(const.EVENT_TRACE_PROPERTIES))
|
|
94
|
+
props = properties_ptr.contents
|
|
95
|
+
|
|
96
|
+
# (2) Fill in basic fields
|
|
97
|
+
props.Wnode.BufferSize = props_size
|
|
98
|
+
props.Wnode.Flags = const.WNODE_FLAG_TRACED_GUID # 0x00020000
|
|
99
|
+
props.Wnode.ClientContext = 1 # QPC clock
|
|
100
|
+
props.BufferSize = 1024
|
|
101
|
+
props.MinimumBuffers = 1
|
|
102
|
+
props.MaximumBuffers = maximum_buffers
|
|
103
|
+
props.MaximumFileSize = 0
|
|
104
|
+
props.LogFileMode = const.EVENT_TRACE_REAL_TIME_MODE # real-time
|
|
105
|
+
props.FlushTimer = 1
|
|
106
|
+
props.EnableFlags = 0
|
|
107
|
+
|
|
108
|
+
# (3) Indicate where in this allocated buffer the strings should go
|
|
109
|
+
struct_size = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
|
|
110
|
+
props.LoggerNameOffset = struct_size
|
|
111
|
+
props.LogFileNameOffset = struct_size + (2 * 1024 * ctypes.sizeof(ctypes.c_wchar))
|
|
112
|
+
|
|
113
|
+
# (4) Copy the session name into the LoggerName space
|
|
114
|
+
logger_name_address = ctypes.addressof(properties_buffer) + props.LoggerNameOffset
|
|
115
|
+
session_name_wchar = ctypes.create_unicode_buffer(session_name)
|
|
116
|
+
ctypes.memmove(
|
|
117
|
+
logger_name_address,
|
|
118
|
+
session_name_wchar,
|
|
119
|
+
len(session_name) * ctypes.sizeof(ctypes.c_wchar)
|
|
120
|
+
)
|
|
104
121
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
# Enable each provider
|
|
112
|
-
for provider_guid in provider_guid_list:
|
|
113
|
-
provider_guid_struct = _string_to_guid(provider_guid)
|
|
114
|
-
status = ctypes.windll.advapi32.EnableTraceEx2(
|
|
115
|
-
session_handle,
|
|
116
|
-
ctypes.byref(provider_guid_struct),
|
|
117
|
-
const.EVENT_CONTROL_CODE_ENABLE_PROVIDER,
|
|
118
|
-
verbosity_mode,
|
|
119
|
-
0,
|
|
120
|
-
0,
|
|
121
|
-
0,
|
|
122
|
-
None
|
|
122
|
+
# (5) Start the session
|
|
123
|
+
session_handle = const.TRACEHANDLE(0)
|
|
124
|
+
status = const.StartTrace(
|
|
125
|
+
ctypes.byref(session_handle),
|
|
126
|
+
session_name, # The session name as an LPCWSTR
|
|
127
|
+
properties_ptr # pointer to EVENT_TRACE_PROPERTIES
|
|
123
128
|
)
|
|
124
129
|
|
|
125
130
|
if status != 0:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
# 183 => ERROR_ALREADY_EXISTS
|
|
132
|
+
if status == 183:
|
|
133
|
+
raise ETWSessionExists(f"ETW session [{session_name}] already exists")
|
|
134
|
+
else:
|
|
135
|
+
raise Exception(f"StartTraceW failed with error code {status}")
|
|
136
|
+
|
|
137
|
+
# (6) If we have providers to enable, enable each
|
|
138
|
+
if provider_guid_list:
|
|
139
|
+
for guid_str in provider_guid_list:
|
|
140
|
+
guid_struct = _string_to_guid(guid_str)
|
|
141
|
+
|
|
142
|
+
# Typically you'd do something like:
|
|
143
|
+
# advapi32.EnableTraceEx2(TRACEHANDLE, PGUID, CONTROL_CODE, LEVEL, KW, KW, TIMEOUT, FILTER)
|
|
144
|
+
|
|
145
|
+
enable_status = const.EnableTraceEx2(
|
|
146
|
+
session_handle, # The session handle
|
|
147
|
+
ctypes.byref(guid_struct), # The provider GUID
|
|
148
|
+
const.EVENT_CONTROL_CODE_ENABLE_PROVIDER, # 1 => enable
|
|
149
|
+
verbosity_mode, # level
|
|
150
|
+
0xFFFFFFFFFFFFFFFF, # matchAnyKeyword
|
|
151
|
+
0, # matchAllKeyword
|
|
152
|
+
0, # timeout
|
|
153
|
+
None # enableParameters (optional)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if enable_status != 0:
|
|
157
|
+
raise Exception(
|
|
158
|
+
f"EnableTraceEx2 failed for provider {guid_str} (error={enable_status})"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
print(f"ETW session '{session_name}' started successfully.")
|
|
162
|
+
return session_handle
|
|
131
163
|
|
|
132
164
|
|
|
133
165
|
# Function to stop and delete ETW session
|
|
@@ -165,6 +197,103 @@ def stop_and_delete_etw_session(session_name: str) -> tuple[bool, int]:
|
|
|
165
197
|
return True, status
|
|
166
198
|
|
|
167
199
|
|
|
200
|
+
@const.EVENT_CALLBACK_TYPE
|
|
201
|
+
def _default_callback(
|
|
202
|
+
event_record_ptr
|
|
203
|
+
):
|
|
204
|
+
"""
|
|
205
|
+
This function will be called by Windows for every incoming ETW event.
|
|
206
|
+
'event_record_ptr' is a pointer to an EVENT_RECORD structure.
|
|
207
|
+
"""
|
|
208
|
+
# Convert pointer to a Python EVENT_RECORD object
|
|
209
|
+
event_record = event_record_ptr.contents
|
|
210
|
+
|
|
211
|
+
# Do something with event_record (e.g., parse via TDH)
|
|
212
|
+
print("Received an ETW event!", event_record.EventHeader.ProviderId)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def start_etw_consumer(
|
|
216
|
+
session_name: str,
|
|
217
|
+
record_queue: queue.Queue
|
|
218
|
+
):
|
|
219
|
+
"""
|
|
220
|
+
Attach to an existing real-time ETW session by 'session_name'
|
|
221
|
+
using the "new" EVENT_RECORD callback approach.
|
|
222
|
+
This call blocks until the ETW session is stopped or an error occurs.
|
|
223
|
+
"""
|
|
224
|
+
# 1) Create a closure callback that references the queue
|
|
225
|
+
# We define an inner function that sees 'record_queue' from outer scope.
|
|
226
|
+
# Then we wrap that in EVENT_CALLBACK_TYPE.
|
|
227
|
+
if record_queue is not None:
|
|
228
|
+
@const.EVENT_CALLBACK_TYPE
|
|
229
|
+
def _queue_callback(event_record_ptr):
|
|
230
|
+
event_record = event_record_ptr.contents
|
|
231
|
+
# # Example: put a simple dict with the provider ID & possibly more info
|
|
232
|
+
# record_queue.put({
|
|
233
|
+
# "provider_id": str(event_record.EventHeader.ProviderId),
|
|
234
|
+
# "process_id": event_record.EventHeader.ProcessId,
|
|
235
|
+
# "thread_id": event_record.EventHeader.ThreadId,
|
|
236
|
+
# # ... more fields or parse them with TdhGetEventInformation, etc.
|
|
237
|
+
# })
|
|
238
|
+
print("Received an ETW event!", event_record.EventHeader.ProviderId)
|
|
239
|
+
record_queue.put(event_record)
|
|
240
|
+
else:
|
|
241
|
+
_queue_callback = _default_callback
|
|
242
|
+
|
|
243
|
+
# Keep a reference to the callback so Python doesn't GC it.
|
|
244
|
+
start_etw_consumer._callback_ref = _queue_callback
|
|
245
|
+
|
|
246
|
+
# Prepare the EVENT_TRACE_LOGFILE structure
|
|
247
|
+
logfile = const.EVENT_TRACE_LOGFILE()
|
|
248
|
+
# You can also do: logfile.LoggerName = session_name
|
|
249
|
+
# If you assign session_name (a Python str), ctypes automatically converts it to a temporary c_wchar_p under the hood.
|
|
250
|
+
# logfile.LoggerName = ctypes.c_wchar_p(session_name)
|
|
251
|
+
logfile.LoggerName = session_name
|
|
252
|
+
logfile.LogFileName = None # Real-time, not from a file
|
|
253
|
+
logfile.ProcessTraceMode = (const.PROCESS_TRACE_MODE_REAL_TIME | const.PROCESS_TRACE_MODE_EVENT_RECORD)
|
|
254
|
+
|
|
255
|
+
# Point to our Python callback
|
|
256
|
+
logfile.EventRecordCallback = const.EVENT_RECORD_CALLBACK(_default_callback)
|
|
257
|
+
|
|
258
|
+
# Open the trace
|
|
259
|
+
trace_handle = const.OpenTrace(ctypes.byref(logfile))
|
|
260
|
+
if trace_handle == const.INVALID_PROCESSTRACE_HANDLE:
|
|
261
|
+
error_code = ctypes.get_last_error()
|
|
262
|
+
raise OSError(f"OpenTrace failed with error code: {error_code}")
|
|
263
|
+
|
|
264
|
+
# trace_handle is actually an unsigned long, but in 64-bit it's a 64-bit handle.
|
|
265
|
+
# We'll create an array of one handle for ProcessTrace:
|
|
266
|
+
trace_handle_array = (ctypes.c_uint64 * 1)(trace_handle)
|
|
267
|
+
|
|
268
|
+
# Blocking call - will not return until the session is stopped (or error).
|
|
269
|
+
status = const.ProcessTrace(
|
|
270
|
+
trace_handle_array,
|
|
271
|
+
1, # HandleCount = 1
|
|
272
|
+
None, # StartTime = None
|
|
273
|
+
None # EndTime = None
|
|
274
|
+
)
|
|
275
|
+
if status != 0:
|
|
276
|
+
raise OSError(f"ProcessTrace failed with error code: {status}")
|
|
277
|
+
|
|
278
|
+
print("ProcessTrace returned. ETW consumer finished.")
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def start_etw_consumer_in_thread(
|
|
282
|
+
session_name: str,
|
|
283
|
+
record_queue: queue.Queue
|
|
284
|
+
):
|
|
285
|
+
"""
|
|
286
|
+
Start an ETW consumer in a separate thread.
|
|
287
|
+
"""
|
|
288
|
+
import threading
|
|
289
|
+
|
|
290
|
+
def _start_etw_consumer():
|
|
291
|
+
start_etw_consumer(session_name, record_queue)
|
|
292
|
+
|
|
293
|
+
thread = threading.Thread(target=_start_etw_consumer)
|
|
294
|
+
thread.start()
|
|
295
|
+
|
|
296
|
+
|
|
168
297
|
def get_all_providers(key_as: Literal['name', 'guid'] = 'name') -> dict:
|
|
169
298
|
"""
|
|
170
299
|
Get all ETW providers available on the system.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=mP4RKVYs6CFRxy4SuFUKdwycVZS11TlGgfMVUl8GYy4,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
|
|
@@ -89,7 +89,7 @@ atomicshop/basics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
89
89
|
atomicshop/basics/ansi_escape_codes.py,sha256=uGVRW01v2O052701sOfAdCaM_GLF_cu_jrssuYiPbzA,3216
|
|
90
90
|
atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
|
|
91
91
|
atomicshop/basics/booleans.py,sha256=V36NaMf8AffhCom_ovQeOZlYcdtGyIcQwWKki6h7O0M,1745
|
|
92
|
-
atomicshop/basics/bytes_arrays.py,sha256=
|
|
92
|
+
atomicshop/basics/bytes_arrays.py,sha256=xfFW9CBQyzf0iXNWpx-EfrxtN3-tiKzuczPzOb6cOdU,7190
|
|
93
93
|
atomicshop/basics/classes.py,sha256=UayCzPs3eynI3wOzSu-2IJSmTwOB4HwPwgVI2F-7_lQ,12648
|
|
94
94
|
atomicshop/basics/dicts.py,sha256=DeYHIh940pMMBrFhpXt4dsigFVYzTrlqWymNo4Pq_Js,14049
|
|
95
95
|
atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
|
|
@@ -115,7 +115,7 @@ atomicshop/etws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
115
115
|
atomicshop/etws/const.py,sha256=v3x_IdCYeSKbCGywiZFOZln80ldpwKW5nuMDuUe51Jg,1257
|
|
116
116
|
atomicshop/etws/providers.py,sha256=CXNx8pYdjtpLIpA66IwrnE64XhY4U5ExnFBMLEb8Uzk,547
|
|
117
117
|
atomicshop/etws/sessions.py,sha256=b_KeiOvgOBJezJokN81TRlrvJiQNJlIWN4Z6UVjuxP0,1335
|
|
118
|
-
atomicshop/etws/trace.py,sha256=
|
|
118
|
+
atomicshop/etws/trace.py,sha256=QcB_NYP-KPOaafYd6jCFdPPQsBu4jzac4QicJdWJAoE,7359
|
|
119
119
|
atomicshop/etws/traces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
120
120
|
atomicshop/etws/traces/trace_dns.py,sha256=WvOZm7KNdP4r6ofkZhUGi9WjtYlkV3mUp_yxita3Qg4,6399
|
|
121
121
|
atomicshop/etws/traces/trace_sysmon_process_creation.py,sha256=OM-bkK38uYMwWLZKNOTDa0Xdk3sO6sqsxoMUIiPvm5g,4656
|
|
@@ -206,8 +206,8 @@ atomicshop/wrappers/ctyping/file_details_winapi.py,sha256=dmdnCMwx4GgVEctiUIyniV
|
|
|
206
206
|
atomicshop/wrappers/ctyping/process_winapi.py,sha256=QcXL-ETtlSSkoT8F7pYle97ubGWsjYp8cx8HxkVMgAc,2762
|
|
207
207
|
atomicshop/wrappers/ctyping/win_console.py,sha256=uTtjkz9rY559AaV0dhyZYUSSEe9cn6Du2DgurdMtX-M,1158
|
|
208
208
|
atomicshop/wrappers/ctyping/etw_winapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
209
|
-
atomicshop/wrappers/ctyping/etw_winapi/const.py,sha256=
|
|
210
|
-
atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py,sha256=
|
|
209
|
+
atomicshop/wrappers/ctyping/etw_winapi/const.py,sha256=W-NMYlJ8RrMxLRjKcatEHe6wJwNxNMGSmYbXRXrlz6o,11223
|
|
210
|
+
atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py,sha256=EMdCjGd3x6aRcHkY2NRNTlrOuC7TY4rhfqfRtTWkbYU,17225
|
|
211
211
|
atomicshop/wrappers/ctyping/msi_windows_installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
212
212
|
atomicshop/wrappers/ctyping/msi_windows_installer/base.py,sha256=Uu9SlWLsQQ6mjE-ek-ptHcmgiI3Ruah9bdZus70EaVY,4884
|
|
213
213
|
atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py,sha256=htzwb2ROYI8yyc82xApStckPS2yCcoyaw32yC15KROs,3285
|
|
@@ -325,8 +325,8 @@ atomicshop/wrappers/socketw/statistics_csv.py,sha256=fgMzDXI0cybwUEqAxprRmY3lqbh
|
|
|
325
325
|
atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
326
326
|
atomicshop/wrappers/winregw/winreg_installed_software.py,sha256=Qzmyktvob1qp6Tjk2DjLfAqr_yXV0sgWzdMW_9kwNjY,2345
|
|
327
327
|
atomicshop/wrappers/winregw/winreg_network.py,sha256=AENV88H1qDidrcpyM9OwEZxX5svfi-Jb4N6FkS1xtqA,8851
|
|
328
|
-
atomicshop-2.19.
|
|
329
|
-
atomicshop-2.19.
|
|
330
|
-
atomicshop-2.19.
|
|
331
|
-
atomicshop-2.19.
|
|
332
|
-
atomicshop-2.19.
|
|
328
|
+
atomicshop-2.19.7.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
329
|
+
atomicshop-2.19.7.dist-info/METADATA,sha256=eWuLCGUrAwVSQ15odcFHEUvTyhT1m9roQxiCYJk1_t8,10630
|
|
330
|
+
atomicshop-2.19.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
331
|
+
atomicshop-2.19.7.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
332
|
+
atomicshop-2.19.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|