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