atomicshop 2.12.22__py3-none-any.whl → 2.12.24__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/addons/process_list/compile.cmd +6 -1
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/addons/process_list/process_list.cpp +8 -1
- atomicshop/etw/sessions.py +14 -0
- atomicshop/etw/trace.py +122 -0
- atomicshop/etw/{dns_trace.py → trace_dns.py} +14 -6
- atomicshop/monitor/change_monitor.py +22 -35
- atomicshop/monitor/checks/dns.py +12 -2
- atomicshop/on_exit.py +175 -0
- atomicshop/process_name_cmd.py +52 -19
- atomicshop/process_poller.py +51 -28
- atomicshop/scheduling.py +12 -2
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +57 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +37 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- {atomicshop-2.12.22.dist-info → atomicshop-2.12.24.dist-info}/METADATA +1 -1
- {atomicshop-2.12.22.dist-info → atomicshop-2.12.24.dist-info}/RECORD +24 -19
- atomicshop/etw/etw.py +0 -61
- atomicshop/monitor/checks/hash.py +0 -56
- {atomicshop-2.12.22.dist-info → atomicshop-2.12.24.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.12.22.dist-info → atomicshop-2.12.24.dist-info}/WHEEL +0 -0
- {atomicshop-2.12.22.dist-info → atomicshop-2.12.24.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
REM Install WIndows 10 SDK before running.
|
|
2
|
+
REM Execute this cmd in:
|
|
3
|
+
REM Start => All Apps => Visual Studio 2022 => x64 Native Tools Command Prompt for VS 2022
|
|
4
|
+
REM Run the prompt as admin.
|
|
5
|
+
REM "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\bin\Hostx64\x64\cl.exe" /LD /EHsc c:\compile\process_list.cpp /Fo:c:\compile /Fe:c:\compile\process_list.dll /I c:\compile\inc\ /link /LIBPATH:c:\compile\lib\ Advapi32.lib ntdll.lib Psapi.lib
|
|
6
|
+
cl /LD /EHsc c:\compile\process_list.cpp /Fo:c:\compile /Fe:c:\compile\process_list.dll /I c:\compile\inc\ /link /LIBPATH:c:\compile\lib\ Advapi32.lib ntdll.lib Psapi.lib
|
|
2
7
|
pause
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
#include <tchar.h>
|
|
4
4
|
#include <iostream>
|
|
5
5
|
#include <tlhelp32.h>
|
|
6
|
+
#include <atomic>
|
|
7
|
+
|
|
8
|
+
std::atomic<bool> cancelRequested(false);
|
|
6
9
|
|
|
7
10
|
typedef LONG NTSTATUS;
|
|
8
11
|
typedef NTSTATUS (*PFN_NT_QUERY_INFORMATION_PROCESS)(HANDLE, UINT, PVOID, ULONG, PULONG);
|
|
@@ -121,6 +124,10 @@ bool GetProcessCommandLine(DWORD dwPid, wchar_t** ppszCmdLine, wchar_t* szProces
|
|
|
121
124
|
|
|
122
125
|
typedef void(*CallbackFunc)(DWORD pid, wchar_t* process_name, wchar_t* cmdline);
|
|
123
126
|
|
|
127
|
+
extern "C" __declspec(dllexport) void RequestCancellation() {
|
|
128
|
+
cancelRequested.store(true);
|
|
129
|
+
}
|
|
130
|
+
|
|
124
131
|
extern "C" __declspec(dllexport) void GetProcessDetails(CallbackFunc callback) {
|
|
125
132
|
if (!EnableDebugPrivilege()) {
|
|
126
133
|
std::wcout << L"Failed to enable debug privilege." << std::endl;
|
|
@@ -156,7 +163,7 @@ extern "C" __declspec(dllexport) void GetProcessDetails(CallbackFunc callback) {
|
|
|
156
163
|
} else {
|
|
157
164
|
callback(pe32.th32ProcessID, szProcessName, NULL);
|
|
158
165
|
}
|
|
159
|
-
} while (Process32NextW(hSnap, &pe32));
|
|
166
|
+
} while (Process32NextW(hSnap, &pe32) && !cancelRequested.load());
|
|
160
167
|
|
|
161
168
|
CloseHandle(hSnap);
|
|
162
169
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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)
|
atomicshop/etw/trace.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import queue
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Import FireEye Event Tracing library.
|
|
5
|
+
import etw
|
|
6
|
+
|
|
7
|
+
from ..print_api import print_api
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EventTrace(etw.ETW):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
providers: list,
|
|
14
|
+
event_callback=None,
|
|
15
|
+
event_id_filters: list = None,
|
|
16
|
+
session_name: str = None
|
|
17
|
+
):
|
|
18
|
+
"""
|
|
19
|
+
:param providers: List of tuples with provider name and provider GUID.
|
|
20
|
+
tuple[0] = provider name
|
|
21
|
+
tuple[1] = provider GUID
|
|
22
|
+
:param event_callback: Reference to the callable callback function that will be called for each occurring event.
|
|
23
|
+
:param event_id_filters: List of event IDs that we want to filter. If not provided, all events will be returned.
|
|
24
|
+
The default in the 'etw.ETW' method is 'None'.
|
|
25
|
+
:param session_name: The name of the session to create. If not provided, a UUID will be generated.
|
|
26
|
+
------------------------------------------
|
|
27
|
+
You should stop the ETW tracing when you are done with it.
|
|
28
|
+
'pywintrace' module starts a new session for ETW tracing, and it will not stop the session when the script
|
|
29
|
+
exits or exception is raised.
|
|
30
|
+
This can cause problems when you want to start the script again, and the session is already running.
|
|
31
|
+
If the session is already running, and you start a new session with the same session name, you will get
|
|
32
|
+
the buffer from when you stopped getting the events from the buffer.
|
|
33
|
+
If you give different session name for new session, the previous session will still continue to run,
|
|
34
|
+
filling the buffer with events, until you will stop getting new events on all sessions or get an
|
|
35
|
+
exception that the buffer is full (WinError 1450).
|
|
36
|
+
|
|
37
|
+
Example to stop the ETW tracing at the end of the script:
|
|
38
|
+
from atomicshop.basics import atexits
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
event_tracing = EventTrace(<Your parameters>)
|
|
42
|
+
atexits.run_callable_on_exit_and_signals(EventTrace.stop)
|
|
43
|
+
"""
|
|
44
|
+
self.event_queue = queue.Queue()
|
|
45
|
+
|
|
46
|
+
# If no callback function is provided, we will use the default one, which will put the event in the queue.
|
|
47
|
+
if not event_callback:
|
|
48
|
+
function_callable = lambda x: self.event_queue.put(x)
|
|
49
|
+
# If a callback function is provided, we will use it.
|
|
50
|
+
else:
|
|
51
|
+
function_callable = lambda x: event_callback(x)
|
|
52
|
+
|
|
53
|
+
# Defining the list of specified ETW providers in 'etw' library format.
|
|
54
|
+
etw_format_providers: list = list()
|
|
55
|
+
for provider in providers:
|
|
56
|
+
etw_format_providers.append(etw.ProviderInfo(provider[0], etw.GUID(provider[1])))
|
|
57
|
+
|
|
58
|
+
super().__init__(
|
|
59
|
+
providers=etw_format_providers, event_callback=function_callable, event_id_filters=event_id_filters,
|
|
60
|
+
session_name=session_name
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def start(self):
|
|
64
|
+
try:
|
|
65
|
+
super().start()
|
|
66
|
+
except OSError as e:
|
|
67
|
+
message = f"PyWinTrace Error: {e}\n" \
|
|
68
|
+
f"PyWinTrace crashed, didn't find solution to this, RESTART computer."
|
|
69
|
+
print_api(message, error_type=True, logger_method='critical')
|
|
70
|
+
sys.exit(1)
|
|
71
|
+
|
|
72
|
+
def stop(self):
|
|
73
|
+
super().stop()
|
|
74
|
+
|
|
75
|
+
def emit(self):
|
|
76
|
+
"""
|
|
77
|
+
The Function will return the next event from the queue.
|
|
78
|
+
The queue is blocking, so if there is no event in the queue, the function will wait until there is one.
|
|
79
|
+
|
|
80
|
+
Usage Example:
|
|
81
|
+
while True:
|
|
82
|
+
dns_dict = dns_trace.emit()
|
|
83
|
+
print(dns_dict)
|
|
84
|
+
|
|
85
|
+
event object:
|
|
86
|
+
event[0]: is the event ID. Example: for DNS Tracing, it is event id 3008.
|
|
87
|
+
event[1]: contains a dictionary with all the event's parameters.
|
|
88
|
+
|
|
89
|
+
:return: etw event object.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
return self.event_queue.get()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def find_sessions_by_provider(provider_name: str):
|
|
96
|
+
"""
|
|
97
|
+
Find ETW session by provider name.
|
|
98
|
+
|
|
99
|
+
:param provider_name: The name of the provider to search for.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_all_providers_from_session(session_name: str):
|
|
106
|
+
"""
|
|
107
|
+
Get all providers that ETW session uses.
|
|
108
|
+
|
|
109
|
+
:param session_name: The name of the session to get providers from.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def stop_session_by_name(session_name: str):
|
|
116
|
+
"""
|
|
117
|
+
Stop ETW session by name.
|
|
118
|
+
|
|
119
|
+
:param session_name: The name of the session to stop.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
return
|
|
@@ -1,12 +1,13 @@
|
|
|
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
|
|
5
5
|
from ..process_poller import ProcessPollerPool
|
|
6
|
+
from ..print_api import print_api
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class DnsTrace:
|
|
9
|
-
def __init__(self, enable_process_poller: bool = False, attrs: list = None):
|
|
10
|
+
def __init__(self, enable_process_poller: bool = False, attrs: list = None, session_name: str = None):
|
|
10
11
|
"""
|
|
11
12
|
DnsTrace class use to trace DNS events from Windows Event Tracing for EventId 3008.
|
|
12
13
|
|
|
@@ -14,6 +15,7 @@ class DnsTrace:
|
|
|
14
15
|
every 100 ms. Since the DNS events doesn't contain the process name and command line, only PID.
|
|
15
16
|
Then DNS events will be enriched with the process name and command line from the process poller.
|
|
16
17
|
:param attrs: List of attributes to return. If None, all attributes will be returned.
|
|
18
|
+
:param session_name: The name of the session to create. If not provided, a UUID will be generated.
|
|
17
19
|
|
|
18
20
|
Usage Example:
|
|
19
21
|
from atomicshop.etw import dns_trace
|
|
@@ -30,10 +32,11 @@ class DnsTrace:
|
|
|
30
32
|
self.enable_process_poller = enable_process_poller
|
|
31
33
|
self.attrs = attrs
|
|
32
34
|
|
|
33
|
-
self.event_trace =
|
|
34
|
-
[(dns.ETW_DNS_INFO['provider_name'], dns.ETW_DNS_INFO['provider_guid'])],
|
|
35
|
+
self.event_trace = trace.EventTrace(
|
|
36
|
+
providers=[(dns.ETW_DNS_INFO['provider_name'], dns.ETW_DNS_INFO['provider_guid'])],
|
|
35
37
|
# lambda x: self.event_queue.put(x),
|
|
36
|
-
event_id_filters=[dns.ETW_DNS_INFO['event_id']]
|
|
38
|
+
event_id_filters=[dns.ETW_DNS_INFO['event_id']],
|
|
39
|
+
session_name=session_name
|
|
37
40
|
)
|
|
38
41
|
|
|
39
42
|
if self.enable_process_poller:
|
|
@@ -117,7 +120,12 @@ class DnsTrace:
|
|
|
117
120
|
event_dict['status'] = 'Error'
|
|
118
121
|
|
|
119
122
|
if self.enable_process_poller:
|
|
120
|
-
|
|
123
|
+
processes = self.process_poller.processes
|
|
124
|
+
|
|
125
|
+
if isinstance(processes, BaseException):
|
|
126
|
+
raise processes
|
|
127
|
+
|
|
128
|
+
event_dict = psutilw.cross_single_connection_with_processes(event_dict, processes)
|
|
121
129
|
# If it was impossible to get the process name from the process poller, get it from psutil.
|
|
122
130
|
# if event_dict['name'].isnumeric():
|
|
123
131
|
# event_dict['name'] = process_name
|
|
@@ -40,28 +40,28 @@ class ChangeMonitor:
|
|
|
40
40
|
'url_playwright_png',
|
|
41
41
|
'url_playwright_jpeg'],
|
|
42
42
|
None] = None,
|
|
43
|
-
object_type_settings: dict = None
|
|
43
|
+
object_type_settings: dict = None,
|
|
44
|
+
etw_session_name: str = None
|
|
44
45
|
):
|
|
45
46
|
"""
|
|
46
47
|
:param object_type: string, type of object to check. The type must be one of the following:
|
|
47
|
-
'file': '
|
|
48
|
-
'dns': '
|
|
49
|
-
'network': '
|
|
50
|
-
'process_running': '
|
|
48
|
+
'file': 'check_object' must contain string of full path to the file.
|
|
49
|
+
'dns': 'check_object' will be none, since the DNS events will be queried from the system.
|
|
50
|
+
'network': 'check_object' will be none, since the network events will be queried from the system.
|
|
51
|
+
'process_running': 'check_object' must contain list of strings of process names to check if they are
|
|
52
|
+
running.
|
|
51
53
|
Example: ['chrome.exe', 'firefox.exe']
|
|
52
54
|
No file is written.
|
|
53
|
-
'
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
'url_playwright_png': 'check_object_list' must contain strings of full URL to a web page. The page will
|
|
60
|
-
be downloaded using 'playwright' library in PNG.
|
|
61
|
-
'url_playwright_jpeg': 'check_object_list' must contain strings of full URL to a web page. The page will
|
|
62
|
-
be downloaded using 'playwright' library in JPEG.
|
|
55
|
+
'url_*': 'check_object' must contain string of full URL to a web page to download.
|
|
56
|
+
'url_urllib': download using 'urllib' library to HTML file.
|
|
57
|
+
'url_playwright_html': download using 'playwright' library to HTML file.
|
|
58
|
+
'url_playwright_pdf': download using 'playwright' library to PDF file.
|
|
59
|
+
'url_playwright_png': download using 'playwright' library to PNG file.
|
|
60
|
+
'url_playwright_jpeg': download using 'playwright' library to JPEG file.
|
|
63
61
|
:param object_type_settings: dict, specific settings for the object type.
|
|
64
62
|
'dns': Check the default settings example in 'DNS__DEFAULT_SETTINGS'.
|
|
63
|
+
'file': Check the default settings example in 'FILE__URL__DEFAULT_SETTINGS'.
|
|
64
|
+
'url_*': Check the default settings example in 'FILE__URL__DEFAULT_SETTINGS'.
|
|
65
65
|
:param check_object: The object to check if changed.
|
|
66
66
|
'dns': empty.
|
|
67
67
|
'network': empty.
|
|
@@ -84,24 +84,10 @@ class ChangeMonitor:
|
|
|
84
84
|
to the input file whether the object was updated.
|
|
85
85
|
False: write to input file each time there is an update, and read each check cycle from the file and not
|
|
86
86
|
from the memory.
|
|
87
|
-
:param
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
'
|
|
91
|
-
'all_objects': disable the DiffChecker features, meaning any new entries will be emitted as is.
|
|
92
|
-
None: will use the default operation type, based on the object type.
|
|
93
|
-
:param hit_statistics_input_file_rotation_cycle_hours:
|
|
94
|
-
float, the amount of hours the input file will be rotated in the 'hit_statistics' operation type.
|
|
95
|
-
str, (only 'midnight' is valid), the input file will be rotated daily at midnight.
|
|
96
|
-
This is valid only for the 'hit_statistics' operation type.
|
|
97
|
-
:param hit_statistics_enable_queue: boolean, if True, the statistics queue will be enabled.
|
|
98
|
-
:param new_objects_hours_then_difference: float, currently works only for the 'dns' object_type.
|
|
99
|
-
This is only for the 'new_objects' operation type.
|
|
100
|
-
If the object is not in the list of objects, it will be added to the list.
|
|
101
|
-
If the object is in the list of objects, it will be ignored.
|
|
102
|
-
After the specified amount of hours, new objects will not be added to the input file list, so each new
|
|
103
|
-
object will be outputted from the function. This is useful for checking new objects that are not
|
|
104
|
-
supposed to be in the list of objects, but you want to know about them.
|
|
87
|
+
:param etw_session_name: string, the name of the ETW session. This should help you manage your ETW sessions
|
|
88
|
+
with logman and other tools: logman query -ets
|
|
89
|
+
If not provided, a default name will be generated.
|
|
90
|
+
'dns': 'AtomicShopDnsTrace'
|
|
105
91
|
|
|
106
92
|
If 'input_directory' is not specified, the 'input_file_name' is not specified, and
|
|
107
93
|
'generate_input_file_name' is False, then the input file will not be used and the object will be stored
|
|
@@ -117,13 +103,14 @@ class ChangeMonitor:
|
|
|
117
103
|
self.input_file_write_only: bool = input_file_write_only
|
|
118
104
|
self.object_type = object_type
|
|
119
105
|
self.object_type_settings: dict = object_type_settings
|
|
106
|
+
self.etw_session_name: str = etw_session_name
|
|
120
107
|
|
|
121
108
|
# === Additional variables ========================================
|
|
122
109
|
|
|
123
110
|
self.checks_instance = None
|
|
124
|
-
self.
|
|
111
|
+
self._setup_check()
|
|
125
112
|
|
|
126
|
-
def
|
|
113
|
+
def _setup_check(self):
|
|
127
114
|
if self.object_type == 'file':
|
|
128
115
|
if not self.object_type_settings:
|
|
129
116
|
self.object_type_settings = FILE__URL__DEFAULT_SETTINGS
|
atomicshop/monitor/checks/dns.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
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
|
|
|
8
8
|
|
|
9
9
|
INPUT_FILE_DEFAULT_NAME: str = 'known_domains.json'
|
|
10
10
|
INPUT_STATISTICS_FILE_DEFAULT_NAME: str = 'dns_statistics.json'
|
|
11
|
+
ETW_DEFAULT_SESSION_NAME: str = 'AtomicShopDnsTrace'
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class DnsCheck:
|
|
@@ -20,8 +21,17 @@ class DnsCheck:
|
|
|
20
21
|
self.diff_checker_aggregation: Union[diff_check.DiffChecker, None] = None
|
|
21
22
|
self.diff_checker_statistics: Union[diff_check.DiffChecker, None] = None
|
|
22
23
|
self.settings: dict = change_monitor_instance.object_type_settings
|
|
24
|
+
|
|
25
|
+
if change_monitor_instance.etw_session_name:
|
|
26
|
+
self.etw_session_name: str = change_monitor_instance.etw_session_name
|
|
27
|
+
else:
|
|
28
|
+
self.etw_session_name: str = ETW_DEFAULT_SESSION_NAME
|
|
29
|
+
|
|
23
30
|
self.fetch_engine: DnsTrace = (
|
|
24
|
-
DnsTrace(
|
|
31
|
+
DnsTrace(
|
|
32
|
+
enable_process_poller=True, attrs=['name', 'cmdline', 'domain', 'query_type'],
|
|
33
|
+
session_name=self.etw_session_name)
|
|
34
|
+
)
|
|
25
35
|
|
|
26
36
|
if self.settings['alert_always'] and self.settings['alert_about_missing_entries_after_learning']:
|
|
27
37
|
raise ValueError(
|
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,19 +1,25 @@
|
|
|
1
|
-
import
|
|
1
|
+
from importlib.resources import files
|
|
2
2
|
import ctypes
|
|
3
3
|
from ctypes import wintypes
|
|
4
4
|
from typing import Literal
|
|
5
|
+
import threading
|
|
5
6
|
|
|
6
7
|
from .basics import list_of_dicts
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
PACKAGE_DLL_PATH = 'addons/process_list/compiled/Win10x64/process_list.dll'
|
|
11
|
+
FULL_DLL_PATH = str(files(__package__).joinpath(PACKAGE_DLL_PATH))
|
|
12
|
+
|
|
13
|
+
|
|
9
14
|
class ProcessNameCmdline:
|
|
10
15
|
def __init__(self, load_dll: bool = False):
|
|
11
16
|
self.load_dll: bool = load_dll
|
|
12
|
-
self.dll_path
|
|
13
|
-
|
|
17
|
+
self.dll_path = FULL_DLL_PATH
|
|
18
|
+
|
|
14
19
|
self.dll = None
|
|
15
20
|
self.callback_output = None
|
|
16
21
|
self.CALLBACKFUNC = None
|
|
22
|
+
self.CALLBACKFUNC_ref = None
|
|
17
23
|
|
|
18
24
|
if self.load_dll:
|
|
19
25
|
self.load()
|
|
@@ -21,12 +27,16 @@ class ProcessNameCmdline:
|
|
|
21
27
|
def load(self):
|
|
22
28
|
"""
|
|
23
29
|
Load the DLL, initialize the callback function and ctypes.
|
|
24
|
-
ctypes.
|
|
30
|
+
ctypes.WINFUNCTYPE is not thread safe. You should load it inside a thread / process and not outside.
|
|
25
31
|
"""
|
|
26
32
|
self.dll = ctypes.windll.LoadLibrary(self.dll_path)
|
|
27
33
|
self.callback_output = OutputList()
|
|
28
34
|
self.CALLBACKFUNC = ctypes.WINFUNCTYPE(None, wintypes.DWORD, wintypes.LPWSTR, wintypes.LPWSTR)
|
|
35
|
+
self.CALLBACKFUNC_ref = self.CALLBACKFUNC(self.callback_output.callback)
|
|
36
|
+
|
|
37
|
+
# Set the argument types for the export functions of the DLL.
|
|
29
38
|
self.dll.GetProcessDetails.argtypes = [self.CALLBACKFUNC]
|
|
39
|
+
self.dll.RequestCancellation.restype = None
|
|
30
40
|
|
|
31
41
|
def get_process_details(
|
|
32
42
|
self,
|
|
@@ -49,23 +59,37 @@ class ProcessNameCmdline:
|
|
|
49
59
|
}
|
|
50
60
|
"""
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
def enumerate_process():
|
|
63
|
+
self.dll.GetProcessDetails(self.CALLBACKFUNC_ref)
|
|
64
|
+
|
|
65
|
+
thread = threading.Thread(target=enumerate_process)
|
|
66
|
+
thread.start()
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# This is needed to stop the thread if the main thread is interrupted.
|
|
70
|
+
# If we execute the 'self.dll.GetProcessDetails(self.CALLBACKFUNC_ref)' directly
|
|
71
|
+
# and we would like to KeyboardInterrupt, we will get an error:
|
|
72
|
+
# Exception ignored on calling ctypes callback function.
|
|
73
|
+
thread.join()
|
|
53
74
|
|
|
54
|
-
|
|
75
|
+
processes = self.callback_output.data
|
|
55
76
|
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
# Clear the callback output list, or it will be appended each time.
|
|
78
|
+
self.callback_output.data = list()
|
|
58
79
|
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
if sort_by:
|
|
81
|
+
processes = list_of_dicts.sort_by_keys(processes, key_list=[sort_by])
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
if as_dict:
|
|
84
|
+
processes = convert_processes_to_dict(processes)
|
|
64
85
|
|
|
65
|
-
|
|
86
|
+
return processes
|
|
87
|
+
except KeyboardInterrupt:
|
|
88
|
+
self.dll.RequestCancellation()
|
|
89
|
+
raise
|
|
66
90
|
|
|
67
91
|
|
|
68
|
-
def convert_processes_to_dict(process_list: dict) -> dict:
|
|
92
|
+
def convert_processes_to_dict(process_list: list[dict]) -> dict:
|
|
69
93
|
"""
|
|
70
94
|
The function will convert the list of processes to dict, while 'pid' values will be converted to key.
|
|
71
95
|
Example:
|
|
@@ -99,14 +123,23 @@ class OutputList:
|
|
|
99
123
|
|
|
100
124
|
def callback(self, pid, process_name, cmdline):
|
|
101
125
|
try:
|
|
126
|
+
process_name_decoded = process_name.decode("utf-16") if isinstance(process_name, bytes) else process_name
|
|
127
|
+
cmdline_decoded = cmdline.decode("utf-16") if isinstance(cmdline, bytes) else (
|
|
128
|
+
cmdline if cmdline else "Error")
|
|
102
129
|
self.data.append({
|
|
103
130
|
"pid": pid,
|
|
104
|
-
"name":
|
|
105
|
-
"cmdline":
|
|
131
|
+
"name": process_name_decoded,
|
|
132
|
+
"cmdline": cmdline_decoded
|
|
106
133
|
})
|
|
107
|
-
except AttributeError:
|
|
134
|
+
# except AttributeError:
|
|
135
|
+
# self.data.append({
|
|
136
|
+
# "pid": pid,
|
|
137
|
+
# "name": process_name,
|
|
138
|
+
# "cmdline": cmdline if cmdline else "Error"
|
|
139
|
+
# })
|
|
140
|
+
except Exception:
|
|
108
141
|
self.data.append({
|
|
109
142
|
"pid": pid,
|
|
110
|
-
"name":
|
|
111
|
-
"cmdline":
|
|
143
|
+
"name": "Error",
|
|
144
|
+
"cmdline": "Error"
|
|
112
145
|
})
|
atomicshop/process_poller.py
CHANGED
|
@@ -135,7 +135,7 @@ class ProcessPollerPool:
|
|
|
135
135
|
self, store_cycles: int = 200,
|
|
136
136
|
interval_seconds: Union[int, float] = 0,
|
|
137
137
|
operation: Literal['thread', 'process'] = 'thread',
|
|
138
|
-
poller_method: Literal['psutil', 'pywin32', 'process_dll'] = 'process_dll'
|
|
138
|
+
poller_method: Literal['psutil', 'pywin32', 'process_dll'] = 'process_dll',
|
|
139
139
|
):
|
|
140
140
|
"""
|
|
141
141
|
:param store_cycles: int, how many cycles to store. Each cycle is polling processes.
|
|
@@ -162,6 +162,14 @@ class ProcessPollerPool:
|
|
|
162
162
|
'psutil': Get the list of processes by 'psutil' library. Resource intensive and slow.
|
|
163
163
|
'pywin32': Get the list of processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
|
|
164
164
|
'process_dll'. Not resource intensive and fast. Probably works only in Windows 10 x64.
|
|
165
|
+
---------------------------------------------
|
|
166
|
+
If there is an exception, ProcessPollerPool.processes will be set to the exception.
|
|
167
|
+
While getting the processes you can use this to execute the exception:
|
|
168
|
+
|
|
169
|
+
processes = ProcessPollerPool.processes
|
|
170
|
+
|
|
171
|
+
if isinstance(processes, BaseException):
|
|
172
|
+
raise processes
|
|
165
173
|
"""
|
|
166
174
|
|
|
167
175
|
self.store_cycles: int = store_cycles
|
|
@@ -193,44 +201,59 @@ class ProcessPollerPool:
|
|
|
193
201
|
def _start_thread(self):
|
|
194
202
|
self.running = True
|
|
195
203
|
# threading.Thread(target=self._worker, args=(self.process_polling_instance,)).start()
|
|
196
|
-
threading.Thread(target=self._worker)
|
|
204
|
+
thread = threading.Thread(target=self._worker)
|
|
205
|
+
thread.daemon = True
|
|
206
|
+
thread.start()
|
|
197
207
|
|
|
198
208
|
def _start_process(self):
|
|
199
209
|
self.running = True
|
|
200
210
|
multiprocessing.Process(target=self._worker).start()
|
|
201
|
-
threading.Thread(target=self._thread_get_queue).start()
|
|
202
211
|
|
|
203
|
-
|
|
212
|
+
thread = threading.Thread(target=self._thread_get_queue)
|
|
213
|
+
thread.daemon = True
|
|
214
|
+
thread.start()
|
|
215
|
+
|
|
204
216
|
def _worker(self):
|
|
205
217
|
# We must initiate the connection inside the thread/process, because it is not thread-safe.
|
|
206
218
|
self.get_processes_list.connect()
|
|
207
219
|
|
|
220
|
+
exception = None
|
|
208
221
|
list_of_processes: list = list()
|
|
209
222
|
while self.running:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
self.
|
|
232
|
-
|
|
233
|
-
|
|
223
|
+
try:
|
|
224
|
+
# If the list is full (to specified 'store_cycles'), remove the first element.
|
|
225
|
+
if len(list_of_processes) == self.store_cycles:
|
|
226
|
+
del list_of_processes[0]
|
|
227
|
+
|
|
228
|
+
# Get the current processes and reinitialize the instance of the dict.
|
|
229
|
+
current_processes: dict = dict(self.get_processes_list.get_processes())
|
|
230
|
+
|
|
231
|
+
# Remove Command lines that contains only numbers, since they are useless.
|
|
232
|
+
for pid, process_info in current_processes.items():
|
|
233
|
+
if process_info['cmdline'].isnumeric():
|
|
234
|
+
current_processes[pid]['cmdline'] = str()
|
|
235
|
+
elif process_info['cmdline'] == 'Error':
|
|
236
|
+
current_processes[pid]['cmdline'] = str()
|
|
237
|
+
|
|
238
|
+
# Append the current processes to the list.
|
|
239
|
+
list_of_processes.append(current_processes)
|
|
240
|
+
|
|
241
|
+
# Merge all dicts in the list to one dict, updating with most recent PIDs.
|
|
242
|
+
self.processes = list_of_dicts.merge_to_dict(list_of_processes)
|
|
243
|
+
|
|
244
|
+
if self.operation == 'process':
|
|
245
|
+
self.queue.put(self.processes)
|
|
246
|
+
|
|
247
|
+
time.sleep(self.interval_seconds)
|
|
248
|
+
except KeyboardInterrupt as e:
|
|
249
|
+
self.running = False
|
|
250
|
+
exception = e
|
|
251
|
+
except Exception as e:
|
|
252
|
+
self.running = False
|
|
253
|
+
exception = e
|
|
254
|
+
|
|
255
|
+
if not self.running:
|
|
256
|
+
self.queue.put(exception)
|
|
234
257
|
|
|
235
258
|
def _thread_get_queue(self):
|
|
236
259
|
while True:
|
atomicshop/scheduling.py
CHANGED
|
@@ -12,7 +12,7 @@ def periodic_task(interval, priority, function_ref, args=(), sched_object=None):
|
|
|
12
12
|
sched_object.run()
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_name=None):
|
|
15
|
+
def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_name=None, daemon=True):
|
|
16
16
|
"""
|
|
17
17
|
The function executes referenced function 'function_ref' with arguments 'args' each 'interval' in a new thread.
|
|
18
18
|
The old thread is closed, each time the new is executed.
|
|
@@ -24,6 +24,10 @@ def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_
|
|
|
24
24
|
:param thread_name: the name of the thread that will be created:
|
|
25
25
|
threading.Thread(target=thread_timer, name=thread_name).start()
|
|
26
26
|
The default parameter for 'Thread' 'name' is 'None', so if you don't specify the name it works as default.
|
|
27
|
+
:param daemon: bool, if True, the thread will be a daemon thread. Default is True.
|
|
28
|
+
Since this is a periodic task, we don't need to wait for the thread to finish, so we can set it to True.
|
|
29
|
+
|
|
30
|
+
:return: thread object.
|
|
27
31
|
"""
|
|
28
32
|
|
|
29
33
|
# If 'kwargs' is not provided, we'll initialize it as an empty dictionary.
|
|
@@ -50,7 +54,13 @@ def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_
|
|
|
50
54
|
time.sleep(interval)
|
|
51
55
|
|
|
52
56
|
# Start in a new thread.
|
|
53
|
-
threading.Thread(target=thread_timer, name=thread_name)
|
|
57
|
+
thread = threading.Thread(target=thread_timer, name=thread_name)
|
|
58
|
+
|
|
59
|
+
if daemon:
|
|
60
|
+
thread.daemon = True
|
|
61
|
+
|
|
62
|
+
thread.start()
|
|
63
|
+
return thread
|
|
54
64
|
|
|
55
65
|
|
|
56
66
|
class ThreadLooper:
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from ctypes import wintypes
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# Load the necessary library
|
|
6
|
+
advapi32 = ctypes.WinDLL('advapi32')
|
|
7
|
+
|
|
8
|
+
# Constants
|
|
9
|
+
EVENT_TRACE_CONTROL_STOP = 1
|
|
10
|
+
WNODE_FLAG_TRACED_GUID = 0x00020000
|
|
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
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from . import const
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# Function to stop and delete ETW session
|
|
6
|
+
def stop_and_delete_etw_session(session_name) -> tuple[bool, int]:
|
|
7
|
+
"""
|
|
8
|
+
Stop and delete ETW session.
|
|
9
|
+
|
|
10
|
+
:param session_name: The name of the session to stop and delete.
|
|
11
|
+
:return: A tuple containing a boolean indicating success and an integer status code.
|
|
12
|
+
True, 0: If the session was stopped and deleted successfully.
|
|
13
|
+
False, <status>: If the session could not be stopped and deleted and the status code, why it failed.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
session_name_unicode = ctypes.create_unicode_buffer(session_name)
|
|
17
|
+
properties_size = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES) + 1024 # Adjust buffer size if needed
|
|
18
|
+
properties = ctypes.create_string_buffer(properties_size)
|
|
19
|
+
|
|
20
|
+
trace_properties = ctypes.cast(properties, ctypes.POINTER(const.EVENT_TRACE_PROPERTIES)).contents
|
|
21
|
+
trace_properties.Wnode.BufferSize = properties_size
|
|
22
|
+
trace_properties.Wnode.Flags = const.WNODE_FLAG_TRACED_GUID
|
|
23
|
+
trace_properties.Wnode.Guid = const.GUID() # Ensure a GUID is provided if necessary
|
|
24
|
+
trace_properties.LoggerNameOffset = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
|
|
25
|
+
|
|
26
|
+
ctypes.memmove(ctypes.addressof(properties) + trace_properties.LoggerNameOffset,
|
|
27
|
+
session_name_unicode, ctypes.sizeof(session_name_unicode))
|
|
28
|
+
|
|
29
|
+
status = const.advapi32.ControlTraceW(
|
|
30
|
+
None, session_name_unicode, ctypes.byref(trace_properties), const.EVENT_TRACE_CONTROL_STOP)
|
|
31
|
+
|
|
32
|
+
if status != 0:
|
|
33
|
+
# print(f"Failed to stop and delete ETW session: {status}")
|
|
34
|
+
return False, status
|
|
35
|
+
else:
|
|
36
|
+
# print("ETW session stopped and deleted successfully.")
|
|
37
|
+
return True, status
|
|
@@ -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=IWq2-3sezAvBANma6AYzXzywVzgI9rJOyd2ZKFBLQ1E,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,17 +21,18 @@ 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
|
|
33
34
|
atomicshop/queues.py,sha256=Al0fdC3ZJmdKfv-PyBeIck9lnfLr82BYchvzr189gsI,640
|
|
34
|
-
atomicshop/scheduling.py,sha256=
|
|
35
|
+
atomicshop/scheduling.py,sha256=NF1_csXwez4RbHoRyUXQg1pGdswGmS1WdSGAQ54m6R8,4550
|
|
35
36
|
atomicshop/script_as_string_processor.py,sha256=JKENiARNuKQ0UegP2GvIVgNPbr6CzGiMElUVTddOX3A,1776
|
|
36
37
|
atomicshop/sound.py,sha256=KSzWRF8dkpEVXmFidIv-Eftc3kET-hQzQOxZRE7rMto,24297
|
|
37
38
|
atomicshop/speech_recognize.py,sha256=55-dIjgkpF93mvJnJuxSFuft5H5eRvGNlUj9BeIOZxk,5903
|
|
@@ -64,11 +65,11 @@ atomicshop/addons/package_setup/CreateWheel.cmd,sha256=hq9aWBSH6iffYlZyaCNrFlA0v
|
|
|
64
65
|
atomicshop/addons/package_setup/Setup in Edit mode.cmd,sha256=299RsExjR8Mup6YyC6rW0qF8lnwa3uIzwk_gYg_R_Ss,176
|
|
65
66
|
atomicshop/addons/package_setup/Setup.cmd,sha256=IMm0PfdARH7CG7h9mbWwmWD9X47l7tddwQ2U4MUxy3A,213
|
|
66
67
|
atomicshop/addons/process_list/ReadMe.txt,sha256=7H-pX7TEkn6-nYSvYc00U6B1JZvBpQvhuVBxCH8IDuc,1895
|
|
67
|
-
atomicshop/addons/process_list/compile.cmd,sha256=
|
|
68
|
-
atomicshop/addons/process_list/process_list.cpp,sha256=
|
|
69
|
-
atomicshop/addons/process_list/compiled/Win10x64/process_list.dll,sha256=
|
|
70
|
-
atomicshop/addons/process_list/compiled/Win10x64/process_list.exp,sha256=
|
|
71
|
-
atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=
|
|
68
|
+
atomicshop/addons/process_list/compile.cmd,sha256=cJSHinOXGl-WkPAq39GdEo5tLLZDg8zdNKIibqf568U,649
|
|
69
|
+
atomicshop/addons/process_list/process_list.cpp,sha256=kcrltiJhzRc2HmKy2Yxdbj7mFLN9O_b4NPDo1xPggM0,5660
|
|
70
|
+
atomicshop/addons/process_list/compiled/Win10x64/process_list.dll,sha256=rl74P7oh4UZyo3cILmtDFznurcHNRMrZ56tOTv5pJqk,276992
|
|
71
|
+
atomicshop/addons/process_list/compiled/Win10x64/process_list.exp,sha256=cbvukITcmtGm5uwOIuq8bKCE_LXIHvkMfLJQpBrJk64,842
|
|
72
|
+
atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=T2Ncs0MwKlAaCq8UKFMvfQAfcJdDx-nWiHVBfglrLIU,2112
|
|
72
73
|
atomicshop/archiver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
74
|
atomicshop/archiver/_search_in_zip.py,sha256=dd8qFSvIhcKmtnPj_uYNJFPmMwZp4tZys0kKgTw_ACw,8385
|
|
74
75
|
atomicshop/archiver/archiver.py,sha256=BomnK7zT-nQXA1z0i2R2aTv8eu88wPx7tf2HtOdbmEc,1280
|
|
@@ -101,8 +102,9 @@ atomicshop/basics/threads.py,sha256=xvgdDJdmgN0wmmARoZ-H7Kvl1GOcEbvgaeGL4M3Hcx8,
|
|
|
101
102
|
atomicshop/basics/timeit_template.py,sha256=fYLrk-X_dhdVtnPU22tarrhhvlggeW6FdKCXM8zkX68,405
|
|
102
103
|
atomicshop/basics/tracebacks.py,sha256=cNfh_oAwF55kSIdqtv3boHZQIoQI8TajxkTnwJwpweI,535
|
|
103
104
|
atomicshop/etw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
|
-
atomicshop/etw/
|
|
105
|
-
atomicshop/etw/
|
|
105
|
+
atomicshop/etw/sessions.py,sha256=7RzlqOEc2zlikKgaNayCnR0IB-51PUrdMyU7uyoBKn4,582
|
|
106
|
+
atomicshop/etw/trace.py,sha256=7xZ-Kj4-hIdVsaqXhpVtP0i8F6dND9ZMe0ZJWq3qtMk,4529
|
|
107
|
+
atomicshop/etw/trace_dns.py,sha256=08xiTGpAAJ3qxobx-9uG49z5pR7BBsys6a_vrMh6JgE,5920
|
|
106
108
|
atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
107
109
|
atomicshop/file_io/csvs.py,sha256=y8cJtnlN-NNxNupzJgSeGq9aQ4wNxYLFPX9vNNlUiIc,5830
|
|
108
110
|
atomicshop/file_io/docxs.py,sha256=6tcYFGp0vRsHR47VwcRqwhdt2DQOwrAUYhrwN996n9U,5117
|
|
@@ -132,11 +134,10 @@ atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256
|
|
|
132
134
|
atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha256=KENDVf9OwXD9gwSh4B1XxACCe7iHYjrvnW1t6F64wdE,695
|
|
133
135
|
atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=1AM49UaFTKA0AHw-k3SV3uH3QbG-o6ux0c-GoWkKNU0,6993
|
|
134
136
|
atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
135
|
-
atomicshop/monitor/change_monitor.py,sha256=
|
|
137
|
+
atomicshop/monitor/change_monitor.py,sha256=dGhk5bJPxLCHa2FOVkort99E7vjVojra9GlvhpcKSqE,7551
|
|
136
138
|
atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
137
|
-
atomicshop/monitor/checks/dns.py,sha256=
|
|
139
|
+
atomicshop/monitor/checks/dns.py,sha256=edO03gtGYb3tn8eV-wAKPELIkdCJvimjkO8PPF-ho-k,7150
|
|
138
140
|
atomicshop/monitor/checks/file.py,sha256=2tIDSlX2KZNc_9i9ji1tcOqupbFTIOj7cKXLyBEDWMk,3263
|
|
139
|
-
atomicshop/monitor/checks/hash.py,sha256=r4mDOdjItN3eyaJk8TVz03f4bQ_uKZ4MDTXagjseazs,2011
|
|
140
141
|
atomicshop/monitor/checks/network.py,sha256=CGZWl4WlQrxayZeVF9JspJXwYA-zWx8ECWTVGSlXc98,3825
|
|
141
142
|
atomicshop/monitor/checks/process_running.py,sha256=x66wd6-l466r8sbRQaIli0yswyGt1dH2DVXkGDL6O0Q,1891
|
|
142
143
|
atomicshop/monitor/checks/url.py,sha256=1PvKt_d7wFg7rDMFpUejAQhj0mqWsmlmrNfjNAV2G4g,4123
|
|
@@ -167,6 +168,9 @@ atomicshop/wrappers/certauthw/certauth.py,sha256=hKedW0DOWlEigSNm8wu4SqHkCQsGJ1t
|
|
|
167
168
|
atomicshop/wrappers/certauthw/certauthw.py,sha256=4WvhjANI7Kzqrr_nKmtA8Kf7B6rute_5wfP65gwQrjw,8082
|
|
168
169
|
atomicshop/wrappers/ctyping/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
169
170
|
atomicshop/wrappers/ctyping/process_winapi.py,sha256=QcXL-ETtlSSkoT8F7pYle97ubGWsjYp8cx8HxkVMgAc,2762
|
|
171
|
+
atomicshop/wrappers/ctyping/etw_winapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
172
|
+
atomicshop/wrappers/ctyping/etw_winapi/const.py,sha256=-wGLmM8FUPrVOGeyEWnizlumkdX60Ziv6v7ANQd9xgM,1712
|
|
173
|
+
atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py,sha256=T5gyTHk3BLTYKSnUKO4PS4h5xIVRrNrHKPL30r_N3HQ,1732
|
|
170
174
|
atomicshop/wrappers/ctyping/msi_windows_installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
171
175
|
atomicshop/wrappers/ctyping/msi_windows_installer/base.py,sha256=Uu9SlWLsQQ6mjE-ek-ptHcmgiI3Ruah9bdZus70EaVY,4884
|
|
172
176
|
atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py,sha256=htzwb2ROYI8yyc82xApStckPS2yCcoyaw32yC15KROs,3285
|
|
@@ -236,6 +240,7 @@ atomicshop/wrappers/psutilw/disks.py,sha256=3ZSVoommKH1TWo37j_83frB-NqXF4Nf5q5mB
|
|
|
236
240
|
atomicshop/wrappers/psutilw/memories.py,sha256=_S0aL8iaoIHebd1vOFrY_T9aROM5Jx2D5CvDh_4j0Vc,528
|
|
237
241
|
atomicshop/wrappers/psutilw/psutilw.py,sha256=G22ZQfGnqX15-feD8KUXfEZO4pFkIEnB8zgPzJ2jc7M,20868
|
|
238
242
|
atomicshop/wrappers/psycopgw/psycopgw.py,sha256=XJvVf0oAUjCHkrYfKeFuGCpfn0Oxj3u4SbKMKA1508E,7118
|
|
243
|
+
atomicshop/wrappers/pywin32w/console.py,sha256=LstHajPLgXp9qQxFNR44QfH10nOnNp3bCJquxaTquns,1175
|
|
239
244
|
atomicshop/wrappers/pywin32w/winshell.py,sha256=i2bKiMldPU7_azsD5xGQDdMwjaM7suKJd3k0Szmcs6c,723
|
|
240
245
|
atomicshop/wrappers/pywin32w/wmi_win32process.py,sha256=qMzXtJ5hBZ5ydAyqpDbSx0nO2RJQL95HdmV5SzNKMhk,6826
|
|
241
246
|
atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -254,8 +259,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
|
|
|
254
259
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
255
260
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
256
261
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
|
|
257
|
-
atomicshop-2.12.
|
|
258
|
-
atomicshop-2.12.
|
|
259
|
-
atomicshop-2.12.
|
|
260
|
-
atomicshop-2.12.
|
|
261
|
-
atomicshop-2.12.
|
|
262
|
+
atomicshop-2.12.24.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
263
|
+
atomicshop-2.12.24.dist-info/METADATA,sha256=wjuGBnA-AbgpeRB_uDgPYzt5XbLF6mIT5yj721N4vi4,10479
|
|
264
|
+
atomicshop-2.12.24.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
265
|
+
atomicshop-2.12.24.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
266
|
+
atomicshop-2.12.24.dist-info/RECORD,,
|
atomicshop/etw/etw.py
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import queue
|
|
2
|
-
|
|
3
|
-
# Import FireEye Event Tracing library.
|
|
4
|
-
import etw
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class EventTrace(etw.ETW):
|
|
8
|
-
def __init__(self, providers: list, event_callback=None, event_id_filters: list = None):
|
|
9
|
-
"""
|
|
10
|
-
:param providers: List of tuples with provider name and provider GUID.
|
|
11
|
-
tuple[0] = provider name
|
|
12
|
-
tuple[1] = provider GUID
|
|
13
|
-
:param event_callback: Reference to the callable callback function that will be called for each occurring event.
|
|
14
|
-
:param event_id_filters: List of event IDs that we want to filter. If not provided, all events will be returned.
|
|
15
|
-
The default in the 'etw.ETW' method is 'None'.
|
|
16
|
-
"""
|
|
17
|
-
self.event_queue = queue.Queue()
|
|
18
|
-
|
|
19
|
-
# If no callback function is provided, we will use the default one, which will put the event in the queue.
|
|
20
|
-
if not event_callback:
|
|
21
|
-
function_callable = lambda x: self.event_queue.put(x)
|
|
22
|
-
# If a callback function is provided, we will use it.
|
|
23
|
-
else:
|
|
24
|
-
function_callable = lambda x: event_callback(x)
|
|
25
|
-
|
|
26
|
-
# Defining the list of specified ETW providers in 'etw' library format.
|
|
27
|
-
etw_format_providers: list = list()
|
|
28
|
-
for provider in providers:
|
|
29
|
-
etw_format_providers.append(etw.ProviderInfo(provider[0], etw.GUID(provider[1])))
|
|
30
|
-
|
|
31
|
-
super().__init__(
|
|
32
|
-
providers=etw_format_providers, event_callback=function_callable, event_id_filters=event_id_filters)
|
|
33
|
-
|
|
34
|
-
def start(self):
|
|
35
|
-
try:
|
|
36
|
-
super().start()
|
|
37
|
-
except OSError as e:
|
|
38
|
-
raise OSError(f"PyWinTrace Error: {e}\n"
|
|
39
|
-
f"PyWinTrace crashed, didn't find solution to this, RESTART computer.")
|
|
40
|
-
|
|
41
|
-
def stop(self):
|
|
42
|
-
super().stop()
|
|
43
|
-
|
|
44
|
-
def emit(self):
|
|
45
|
-
"""
|
|
46
|
-
The Function will return the next event from the queue.
|
|
47
|
-
The queue is blocking, so if there is no event in the queue, the function will wait until there is one.
|
|
48
|
-
|
|
49
|
-
Usage Example:
|
|
50
|
-
while True:
|
|
51
|
-
dns_dict = dns_trace.emit()
|
|
52
|
-
print(dns_dict)
|
|
53
|
-
|
|
54
|
-
event object:
|
|
55
|
-
event[0]: is the event ID. Example: for DNS Tracing, it is event id 3008.
|
|
56
|
-
event[1]: contains a dictionary with all the event's parameters.
|
|
57
|
-
|
|
58
|
-
:return: etw event object.
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
return self.event_queue.get()
|
|
@@ -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
|