atomicshop 2.12.21__py3-none-any.whl → 2.12.23__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/basics/atexits.py +64 -0
- atomicshop/etw/dns_trace.py +12 -4
- atomicshop/etw/etw.py +60 -2
- atomicshop/file_io/csvs.py +6 -2
- atomicshop/monitor/change_monitor.py +22 -35
- atomicshop/monitor/checks/dns.py +11 -1
- atomicshop/process_name_cmd.py +53 -19
- atomicshop/process_poller.py +53 -29
- atomicshop/scheduling.py +12 -2
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/loggingw/reading.py +6 -1
- {atomicshop-2.12.21.dist-info → atomicshop-2.12.23.dist-info}/METADATA +1 -1
- {atomicshop-2.12.21.dist-info → atomicshop-2.12.23.dist-info}/RECORD +22 -21
- {atomicshop-2.12.21.dist-info → atomicshop-2.12.23.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.12.21.dist-info → atomicshop-2.12.23.dist-info}/WHEEL +0 -0
- {atomicshop-2.12.21.dist-info → atomicshop-2.12.23.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,64 @@
|
|
|
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)
|
atomicshop/etw/dns_trace.py
CHANGED
|
@@ -3,10 +3,11 @@ 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
|
|
@@ -31,9 +33,10 @@ class DnsTrace:
|
|
|
31
33
|
self.attrs = attrs
|
|
32
34
|
|
|
33
35
|
self.event_trace = etw.EventTrace(
|
|
34
|
-
[(dns.ETW_DNS_INFO['provider_name'], dns.ETW_DNS_INFO['provider_guid'])],
|
|
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
|
atomicshop/etw/etw.py
CHANGED
|
@@ -3,9 +3,18 @@ import queue
|
|
|
3
3
|
# Import FireEye Event Tracing library.
|
|
4
4
|
import etw
|
|
5
5
|
|
|
6
|
+
from ..basics import atexits
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class EventTrace(etw.ETW):
|
|
8
|
-
def __init__(
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
providers: list,
|
|
13
|
+
event_callback=None,
|
|
14
|
+
event_id_filters: list = None,
|
|
15
|
+
session_name: str = None,
|
|
16
|
+
stop_etw_tracing_on_exit: bool = False
|
|
17
|
+
):
|
|
9
18
|
"""
|
|
10
19
|
:param providers: List of tuples with provider name and provider GUID.
|
|
11
20
|
tuple[0] = provider name
|
|
@@ -13,8 +22,21 @@ class EventTrace(etw.ETW):
|
|
|
13
22
|
:param event_callback: Reference to the callable callback function that will be called for each occurring event.
|
|
14
23
|
:param event_id_filters: List of event IDs that we want to filter. If not provided, all events will be returned.
|
|
15
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
|
+
:param stop_etw_tracing_on_exit: If True, the ETW tracing will be stopped when the script exits.
|
|
27
|
+
'pywintrace' module starts a new session for ETW tracing, and it will not stop the session when the script
|
|
28
|
+
exits or exception is raised.
|
|
29
|
+
This can cause problems when you want to start the script again, and the session is already running.
|
|
30
|
+
If the session is already running, and you start a new session with the same session name, you will get
|
|
31
|
+
the buffer from when you stopped getting the events from the buffer.
|
|
32
|
+
If you give different session name for new session, the previous session will still continue to run,
|
|
33
|
+
filling the buffer with events, until you will stop getting new events on all sessions or get an
|
|
34
|
+
exception that the buffer is full (WinError 1450).
|
|
16
35
|
"""
|
|
17
36
|
self.event_queue = queue.Queue()
|
|
37
|
+
self.stop_etw_tracing_on_exit: bool = stop_etw_tracing_on_exit
|
|
38
|
+
|
|
39
|
+
self._set_atexit_and_signals()
|
|
18
40
|
|
|
19
41
|
# If no callback function is provided, we will use the default one, which will put the event in the queue.
|
|
20
42
|
if not event_callback:
|
|
@@ -29,7 +51,9 @@ class EventTrace(etw.ETW):
|
|
|
29
51
|
etw_format_providers.append(etw.ProviderInfo(provider[0], etw.GUID(provider[1])))
|
|
30
52
|
|
|
31
53
|
super().__init__(
|
|
32
|
-
providers=etw_format_providers, event_callback=function_callable, event_id_filters=event_id_filters
|
|
54
|
+
providers=etw_format_providers, event_callback=function_callable, event_id_filters=event_id_filters,
|
|
55
|
+
session_name=session_name
|
|
56
|
+
)
|
|
33
57
|
|
|
34
58
|
def start(self):
|
|
35
59
|
try:
|
|
@@ -59,3 +83,37 @@ class EventTrace(etw.ETW):
|
|
|
59
83
|
"""
|
|
60
84
|
|
|
61
85
|
return self.event_queue.get()
|
|
86
|
+
|
|
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
|
+
|
|
92
|
+
def find_sessions_by_provider(provider_name: str):
|
|
93
|
+
"""
|
|
94
|
+
Find ETW session by provider name.
|
|
95
|
+
|
|
96
|
+
:param provider_name: The name of the provider to search for.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_all_providers_from_session(session_name: str):
|
|
103
|
+
"""
|
|
104
|
+
Get all providers that ETW session uses.
|
|
105
|
+
|
|
106
|
+
:param session_name: The name of the session to get providers from.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def stop_session_by_name(session_name: str):
|
|
113
|
+
"""
|
|
114
|
+
Stop ETW session by name.
|
|
115
|
+
|
|
116
|
+
:param session_name: The name of the session to stop.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
return
|
atomicshop/file_io/csvs.py
CHANGED
|
@@ -105,7 +105,11 @@ def write_list_to_csv(
|
|
|
105
105
|
mode: str = 'w'
|
|
106
106
|
) -> None:
|
|
107
107
|
"""
|
|
108
|
-
|
|
108
|
+
This function got dual purpose:
|
|
109
|
+
1. Write list object that each iteration of it contains list object with same length.
|
|
110
|
+
2. Write list object that each iteration of it contains dict object with same keys and different values.
|
|
111
|
+
The dictionary inside the function will be identified by the first iteration of the list.
|
|
112
|
+
Other objects (inside the provided list) than dictionary will be identified as regular objects.
|
|
109
113
|
|
|
110
114
|
:param file_path: Full file path to CSV file.
|
|
111
115
|
:param content_list: List object that each iteration contains dictionary with same keys and different values.
|
|
@@ -113,7 +117,7 @@ def write_list_to_csv(
|
|
|
113
117
|
:return: None.
|
|
114
118
|
"""
|
|
115
119
|
|
|
116
|
-
with open(file_path, mode=mode) as csv_file:
|
|
120
|
+
with open(file_path, mode=mode, newline='') as csv_file:
|
|
117
121
|
if len(content_list) > 0 and isinstance(content_list[0], dict):
|
|
118
122
|
# Treat the list as list of dictionaries.
|
|
119
123
|
header = content_list[0].keys()
|
|
@@ -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
|
@@ -8,6 +8,7 @@ from ...import diff_check
|
|
|
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/process_name_cmd.py
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
from importlib import resources
|
|
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
|
+
|
|
12
|
+
|
|
9
13
|
class ProcessNameCmdline:
|
|
10
14
|
def __init__(self, load_dll: bool = False):
|
|
11
15
|
self.load_dll: bool = load_dll
|
|
12
|
-
|
|
13
|
-
__package__, 'addons/process_list/compiled/Win10x64/process_list.dll')
|
|
16
|
+
|
|
14
17
|
self.dll = None
|
|
15
18
|
self.callback_output = None
|
|
16
19
|
self.CALLBACKFUNC = None
|
|
20
|
+
self.CALLBACKFUNC_ref = None
|
|
21
|
+
|
|
22
|
+
with resources.path(__package__, PACKAGE_DLL_PATH) as dll_path:
|
|
23
|
+
self.dll_path = str(dll_path)
|
|
17
24
|
|
|
18
25
|
if self.load_dll:
|
|
19
26
|
self.load()
|
|
@@ -21,12 +28,16 @@ class ProcessNameCmdline:
|
|
|
21
28
|
def load(self):
|
|
22
29
|
"""
|
|
23
30
|
Load the DLL, initialize the callback function and ctypes.
|
|
24
|
-
ctypes.
|
|
31
|
+
ctypes.WINFUNCTYPE is not thread safe. You should load it inside a thread / process and not outside.
|
|
25
32
|
"""
|
|
26
33
|
self.dll = ctypes.windll.LoadLibrary(self.dll_path)
|
|
27
34
|
self.callback_output = OutputList()
|
|
28
35
|
self.CALLBACKFUNC = ctypes.WINFUNCTYPE(None, wintypes.DWORD, wintypes.LPWSTR, wintypes.LPWSTR)
|
|
36
|
+
self.CALLBACKFUNC_ref = self.CALLBACKFUNC(self.callback_output.callback)
|
|
37
|
+
|
|
38
|
+
# Set the argument types for the export functions of the DLL.
|
|
29
39
|
self.dll.GetProcessDetails.argtypes = [self.CALLBACKFUNC]
|
|
40
|
+
self.dll.RequestCancellation.restype = None
|
|
30
41
|
|
|
31
42
|
def get_process_details(
|
|
32
43
|
self,
|
|
@@ -49,23 +60,37 @@ class ProcessNameCmdline:
|
|
|
49
60
|
}
|
|
50
61
|
"""
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
def enumerate_process():
|
|
64
|
+
self.dll.GetProcessDetails(self.CALLBACKFUNC_ref)
|
|
65
|
+
|
|
66
|
+
thread = threading.Thread(target=enumerate_process)
|
|
67
|
+
thread.start()
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
# This is needed to stop the thread if the main thread is interrupted.
|
|
71
|
+
# If we execute the 'self.dll.GetProcessDetails(self.CALLBACKFUNC_ref)' directly
|
|
72
|
+
# and we would like to KeyboardInterrupt, we will get an error:
|
|
73
|
+
# Exception ignored on calling ctypes callback function.
|
|
74
|
+
thread.join()
|
|
53
75
|
|
|
54
|
-
|
|
76
|
+
processes = self.callback_output.data
|
|
55
77
|
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
# Clear the callback output list, or it will be appended each time.
|
|
79
|
+
self.callback_output.data = list()
|
|
58
80
|
|
|
59
|
-
|
|
60
|
-
|
|
81
|
+
if sort_by:
|
|
82
|
+
processes = list_of_dicts.sort_by_keys(processes, key_list=[sort_by])
|
|
61
83
|
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
if as_dict:
|
|
85
|
+
processes = convert_processes_to_dict(processes)
|
|
64
86
|
|
|
65
|
-
|
|
87
|
+
return processes
|
|
88
|
+
except KeyboardInterrupt:
|
|
89
|
+
self.dll.RequestCancellation()
|
|
90
|
+
raise
|
|
66
91
|
|
|
67
92
|
|
|
68
|
-
def convert_processes_to_dict(process_list: dict) -> dict:
|
|
93
|
+
def convert_processes_to_dict(process_list: list[dict]) -> dict:
|
|
69
94
|
"""
|
|
70
95
|
The function will convert the list of processes to dict, while 'pid' values will be converted to key.
|
|
71
96
|
Example:
|
|
@@ -99,14 +124,23 @@ class OutputList:
|
|
|
99
124
|
|
|
100
125
|
def callback(self, pid, process_name, cmdline):
|
|
101
126
|
try:
|
|
127
|
+
process_name_decoded = process_name.decode("utf-16") if isinstance(process_name, bytes) else process_name
|
|
128
|
+
cmdline_decoded = cmdline.decode("utf-16") if isinstance(cmdline, bytes) else (
|
|
129
|
+
cmdline if cmdline else "Error")
|
|
102
130
|
self.data.append({
|
|
103
131
|
"pid": pid,
|
|
104
|
-
"name":
|
|
105
|
-
"cmdline":
|
|
132
|
+
"name": process_name_decoded,
|
|
133
|
+
"cmdline": cmdline_decoded
|
|
106
134
|
})
|
|
107
|
-
except AttributeError:
|
|
135
|
+
# except AttributeError:
|
|
136
|
+
# self.data.append({
|
|
137
|
+
# "pid": pid,
|
|
138
|
+
# "name": process_name,
|
|
139
|
+
# "cmdline": cmdline if cmdline else "Error"
|
|
140
|
+
# })
|
|
141
|
+
except Exception:
|
|
108
142
|
self.data.append({
|
|
109
143
|
"pid": pid,
|
|
110
|
-
"name":
|
|
111
|
-
"cmdline":
|
|
144
|
+
"name": "Error",
|
|
145
|
+
"cmdline": "Error"
|
|
112
146
|
})
|
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,60 @@ 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
|
-
multiprocessing.
|
|
201
|
-
|
|
210
|
+
stopping_queue = multiprocessing.Queue()
|
|
211
|
+
multiprocessing.Process(target=self._worker, args=(stopping_queue,)).start()
|
|
212
|
+
|
|
213
|
+
thread = threading.Thread(target=self._thread_get_queue)
|
|
214
|
+
thread.daemon = True
|
|
215
|
+
thread.start()
|
|
202
216
|
|
|
203
|
-
# def _worker(self, process_polling_instance):
|
|
204
217
|
def _worker(self):
|
|
205
218
|
# We must initiate the connection inside the thread/process, because it is not thread-safe.
|
|
206
219
|
self.get_processes_list.connect()
|
|
207
220
|
|
|
221
|
+
exception = None
|
|
208
222
|
list_of_processes: list = list()
|
|
209
223
|
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
|
-
|
|
224
|
+
try:
|
|
225
|
+
# If the list is full (to specified 'store_cycles'), remove the first element.
|
|
226
|
+
if len(list_of_processes) == self.store_cycles:
|
|
227
|
+
del list_of_processes[0]
|
|
228
|
+
|
|
229
|
+
# Get the current processes and reinitialize the instance of the dict.
|
|
230
|
+
current_processes: dict = dict(self.get_processes_list.get_processes())
|
|
231
|
+
|
|
232
|
+
# Remove Command lines that contains only numbers, since they are useless.
|
|
233
|
+
for pid, process_info in current_processes.items():
|
|
234
|
+
if process_info['cmdline'].isnumeric():
|
|
235
|
+
current_processes[pid]['cmdline'] = str()
|
|
236
|
+
elif process_info['cmdline'] == 'Error':
|
|
237
|
+
current_processes[pid]['cmdline'] = str()
|
|
238
|
+
|
|
239
|
+
# Append the current processes to the list.
|
|
240
|
+
list_of_processes.append(current_processes)
|
|
241
|
+
|
|
242
|
+
# Merge all dicts in the list to one dict, updating with most recent PIDs.
|
|
243
|
+
self.processes = list_of_dicts.merge_to_dict(list_of_processes)
|
|
244
|
+
|
|
245
|
+
if self.operation == 'process':
|
|
246
|
+
self.queue.put(self.processes)
|
|
247
|
+
|
|
248
|
+
time.sleep(self.interval_seconds)
|
|
249
|
+
except KeyboardInterrupt as e:
|
|
250
|
+
self.running = False
|
|
251
|
+
exception = e
|
|
252
|
+
except Exception as e:
|
|
253
|
+
self.running = False
|
|
254
|
+
exception = e
|
|
255
|
+
|
|
256
|
+
if not self.running:
|
|
257
|
+
self.queue.put(exception)
|
|
234
258
|
|
|
235
259
|
def _thread_get_queue(self):
|
|
236
260
|
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:
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import time
|
|
2
|
-
from typing import Union
|
|
3
1
|
import os
|
|
4
2
|
|
|
5
|
-
from . import
|
|
6
|
-
from .. import config_fact
|
|
3
|
+
from . import fw_files, virtual_file_path, file_object, analysis
|
|
4
|
+
from .. import config_fact
|
|
7
5
|
from ....print_api import print_api, print_status_of_list
|
|
8
|
-
from ....file_io import
|
|
6
|
+
from ....file_io import jsons, csvs
|
|
9
7
|
from ....basics import dicts
|
|
10
|
-
from .... import
|
|
8
|
+
from .... import ip_addresses
|
|
11
9
|
from ...psycopgw import psycopgw
|
|
12
10
|
|
|
13
11
|
|
|
@@ -113,6 +113,10 @@ def get_logs_paths(
|
|
|
113
113
|
logs_files = []
|
|
114
114
|
else:
|
|
115
115
|
logs_files = [logs_files[-2]]
|
|
116
|
+
# If there is only one file, meaning it is the current day log.
|
|
117
|
+
# If the 'previous_day_only' is True, then there are no previous day logs to output.
|
|
118
|
+
elif len(logs_files) == 1 and previous_day_only:
|
|
119
|
+
logs_files = []
|
|
116
120
|
|
|
117
121
|
return logs_files
|
|
118
122
|
|
|
@@ -309,7 +313,8 @@ def get_latest_lines(
|
|
|
309
313
|
log_type='csv',
|
|
310
314
|
previous_day_only=True
|
|
311
315
|
)[0]['file_path']
|
|
312
|
-
|
|
316
|
+
# If you get IndexError, it means that there are no previous day logs to read.
|
|
317
|
+
except IndexError:
|
|
313
318
|
pass
|
|
314
319
|
|
|
315
320
|
# Count all the rotated files.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=8aamirvtHe04uZCHVaHjJnr7Y7nVlrXMflv_x72OTIo,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
|
|
@@ -25,13 +25,13 @@ atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erX
|
|
|
25
25
|
atomicshop/permissions.py,sha256=P6tiUKV-Gw-c3ePEVsst9bqWaHJbB4ZlJB4xbDYVpEs,4436
|
|
26
26
|
atomicshop/print_api.py,sha256=DhbCQd0MWZZ5GYEk4oTu1opRFC-b31g1VWZgTGewG2Y,11568
|
|
27
27
|
atomicshop/process.py,sha256=Zgb4CUjy9gIBaawvtCOEcxGUCqvqPyARk0lpBjRzxWE,15950
|
|
28
|
-
atomicshop/process_name_cmd.py,sha256=
|
|
29
|
-
atomicshop/process_poller.py,sha256=
|
|
28
|
+
atomicshop/process_name_cmd.py,sha256=YmAfxZ3fhND58ItJ1fA1C6tPqyNUBlolOM2smKVDK2o,5167
|
|
29
|
+
atomicshop/process_poller.py,sha256=Ya08tLe6NfbJ-U3EZWZ3j-FFqkDSy8zaTu1QeEsWC98,11754
|
|
30
30
|
atomicshop/python_file_patcher.py,sha256=kd3rBWvTcosLEk-7TycNdfKW9fZbe161iVwmH4niUo0,5515
|
|
31
31
|
atomicshop/python_functions.py,sha256=zJg4ogUwECxrDD7xdDN5JikIUctITM5lsyabr_ZNsRw,4435
|
|
32
32
|
atomicshop/question_answer_engine.py,sha256=DuOn7QEgKKfqZu2cR8mVeFIfFgayfBHiW-jY2VPq_Fo,841
|
|
33
33
|
atomicshop/queues.py,sha256=Al0fdC3ZJmdKfv-PyBeIck9lnfLr82BYchvzr189gsI,640
|
|
34
|
-
atomicshop/scheduling.py,sha256=
|
|
34
|
+
atomicshop/scheduling.py,sha256=NF1_csXwez4RbHoRyUXQg1pGdswGmS1WdSGAQ54m6R8,4550
|
|
35
35
|
atomicshop/script_as_string_processor.py,sha256=JKENiARNuKQ0UegP2GvIVgNPbr6CzGiMElUVTddOX3A,1776
|
|
36
36
|
atomicshop/sound.py,sha256=KSzWRF8dkpEVXmFidIv-Eftc3kET-hQzQOxZRE7rMto,24297
|
|
37
37
|
atomicshop/speech_recognize.py,sha256=55-dIjgkpF93mvJnJuxSFuft5H5eRvGNlUj9BeIOZxk,5903
|
|
@@ -64,11 +64,11 @@ atomicshop/addons/package_setup/CreateWheel.cmd,sha256=hq9aWBSH6iffYlZyaCNrFlA0v
|
|
|
64
64
|
atomicshop/addons/package_setup/Setup in Edit mode.cmd,sha256=299RsExjR8Mup6YyC6rW0qF8lnwa3uIzwk_gYg_R_Ss,176
|
|
65
65
|
atomicshop/addons/package_setup/Setup.cmd,sha256=IMm0PfdARH7CG7h9mbWwmWD9X47l7tddwQ2U4MUxy3A,213
|
|
66
66
|
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=
|
|
67
|
+
atomicshop/addons/process_list/compile.cmd,sha256=cJSHinOXGl-WkPAq39GdEo5tLLZDg8zdNKIibqf568U,649
|
|
68
|
+
atomicshop/addons/process_list/process_list.cpp,sha256=kcrltiJhzRc2HmKy2Yxdbj7mFLN9O_b4NPDo1xPggM0,5660
|
|
69
|
+
atomicshop/addons/process_list/compiled/Win10x64/process_list.dll,sha256=rl74P7oh4UZyo3cILmtDFznurcHNRMrZ56tOTv5pJqk,276992
|
|
70
|
+
atomicshop/addons/process_list/compiled/Win10x64/process_list.exp,sha256=cbvukITcmtGm5uwOIuq8bKCE_LXIHvkMfLJQpBrJk64,842
|
|
71
|
+
atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=T2Ncs0MwKlAaCq8UKFMvfQAfcJdDx-nWiHVBfglrLIU,2112
|
|
72
72
|
atomicshop/archiver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
73
|
atomicshop/archiver/_search_in_zip.py,sha256=dd8qFSvIhcKmtnPj_uYNJFPmMwZp4tZys0kKgTw_ACw,8385
|
|
74
74
|
atomicshop/archiver/archiver.py,sha256=BomnK7zT-nQXA1z0i2R2aTv8eu88wPx7tf2HtOdbmEc,1280
|
|
@@ -79,6 +79,7 @@ atomicshop/archiver/zips.py,sha256=k742K1bEDtc_4N44j_Waebi-uOkxxavqltvV6q-BLW4,1
|
|
|
79
79
|
atomicshop/basics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
80
|
atomicshop/basics/ansi_escape_codes.py,sha256=WtIkm-BjSZS5J5irDUdAMBNvdX-qXFZcTX98jcBMpJE,3140
|
|
81
81
|
atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
|
|
82
|
+
atomicshop/basics/atexits.py,sha256=w7ge8sjuqK2_jdkdP4F4OCltkenQud8AhVEzxhFDiao,2145
|
|
82
83
|
atomicshop/basics/booleans.py,sha256=va3LYIaSOhjdifW4ZEesnIQxBICNHyQjUAkYelzchhE,2047
|
|
83
84
|
atomicshop/basics/bytes_arrays.py,sha256=WvSRDhIGt1ywF95t-yNgpxLm1nlZUbM1Dz6QckcyE8Y,5915
|
|
84
85
|
atomicshop/basics/classes.py,sha256=EijW_g4EhdNBnKPMG3nT3HjFspTchtM7to6zm9Ad_Mk,9771
|
|
@@ -101,10 +102,10 @@ 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/dns_trace.py,sha256=
|
|
105
|
-
atomicshop/etw/etw.py,sha256=
|
|
105
|
+
atomicshop/etw/dns_trace.py,sha256=rv1A5akgebLzoT5EcvyuNwIztnA2xJYZlZlAbINFe88,5916
|
|
106
|
+
atomicshop/etw/etw.py,sha256=WEs1aqXowH-tKUbaJphhc1hQvpi4SqU3ltyBg5wGM0c,4470
|
|
106
107
|
atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
107
|
-
atomicshop/file_io/csvs.py,sha256=
|
|
108
|
+
atomicshop/file_io/csvs.py,sha256=y8cJtnlN-NNxNupzJgSeGq9aQ4wNxYLFPX9vNNlUiIc,5830
|
|
108
109
|
atomicshop/file_io/docxs.py,sha256=6tcYFGp0vRsHR47VwcRqwhdt2DQOwrAUYhrwN996n9U,5117
|
|
109
110
|
atomicshop/file_io/file_io.py,sha256=FOZ6_PjOASxSDHESe4fwDv5miXYR10OHTxkxtEHoZYQ,6555
|
|
110
111
|
atomicshop/file_io/jsons.py,sha256=q9ZU8slBKnHLrtn3TnbK1qxrRpj5ZvCm6AlsFzoANjo,5303
|
|
@@ -132,9 +133,9 @@ atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256
|
|
|
132
133
|
atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha256=KENDVf9OwXD9gwSh4B1XxACCe7iHYjrvnW1t6F64wdE,695
|
|
133
134
|
atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=1AM49UaFTKA0AHw-k3SV3uH3QbG-o6ux0c-GoWkKNU0,6993
|
|
134
135
|
atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
135
|
-
atomicshop/monitor/change_monitor.py,sha256=
|
|
136
|
+
atomicshop/monitor/change_monitor.py,sha256=dGhk5bJPxLCHa2FOVkort99E7vjVojra9GlvhpcKSqE,7551
|
|
136
137
|
atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
137
|
-
atomicshop/monitor/checks/dns.py,sha256=
|
|
138
|
+
atomicshop/monitor/checks/dns.py,sha256=v3Pgdi8tmBmGAs7x7u6N5IiGyTv6GZOLEbrVSZZGE6s,7150
|
|
138
139
|
atomicshop/monitor/checks/file.py,sha256=2tIDSlX2KZNc_9i9ji1tcOqupbFTIOj7cKXLyBEDWMk,3263
|
|
139
140
|
atomicshop/monitor/checks/hash.py,sha256=r4mDOdjItN3eyaJk8TVz03f4bQ_uKZ4MDTXagjseazs,2011
|
|
140
141
|
atomicshop/monitor/checks/network.py,sha256=CGZWl4WlQrxayZeVF9JspJXwYA-zWx8ECWTVGSlXc98,3825
|
|
@@ -201,7 +202,7 @@ atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py,sha2
|
|
|
201
202
|
atomicshop/wrappers/factw/postgresql/__init__.py,sha256=xMBn2d3Exo23IPP2F_9-SXmOlhFbwWDgS9KwozSTjA0,162
|
|
202
203
|
atomicshop/wrappers/factw/postgresql/analysis.py,sha256=2Rxzy2jyq3zEKIo53z8VkjuslKE_i5mq2ZpmJAvyd6U,716
|
|
203
204
|
atomicshop/wrappers/factw/postgresql/file_object.py,sha256=VRiCXnsd6yDbnsE-TEKYPC-gkAgFVkE6rygRrJLQShI,713
|
|
204
|
-
atomicshop/wrappers/factw/postgresql/firmware.py,sha256=
|
|
205
|
+
atomicshop/wrappers/factw/postgresql/firmware.py,sha256=ywPn8yBAliX4FO7ZqfrPsGPZMxBTPLSwEmex4pz1CZ8,10668
|
|
205
206
|
atomicshop/wrappers/factw/postgresql/fw_files.py,sha256=P1jq4AAZa7fygWdEZtFJOnfz4tyqmPpvFzEMDKrCRkU,1291
|
|
206
207
|
atomicshop/wrappers/factw/postgresql/included_files.py,sha256=sn5YhLkrsvjhrVSA8O8YUNfbqR9STprSuQGEnHsK0jE,1025
|
|
207
208
|
atomicshop/wrappers/factw/postgresql/virtual_file_path.py,sha256=iR68A_My_ohgRcYdueMaQF9EHOgBRN3bIi8Nq59g3kc,1098
|
|
@@ -218,7 +219,7 @@ atomicshop/wrappers/loggingw/formatters.py,sha256=mUtcJJfmhLNrwUVYShXTmdu40dBaJu
|
|
|
218
219
|
atomicshop/wrappers/loggingw/handlers.py,sha256=2A_3Qy1B0RvVWZmQocAB6CmpqlXoKJ-yi6iBWG2jNLo,8274
|
|
219
220
|
atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1kvbhG-TDog8,2374
|
|
220
221
|
atomicshop/wrappers/loggingw/loggingw.py,sha256=m6YySEedP3_4Ik1S_uGMxETSbmRkmMYmAZxhHBlXSlo,16616
|
|
221
|
-
atomicshop/wrappers/loggingw/reading.py,sha256=
|
|
222
|
+
atomicshop/wrappers/loggingw/reading.py,sha256=iRXwPHhwkzuBFz4nlRdO9fpfxnNCYRE09r8JvtqTcao,15671
|
|
222
223
|
atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
223
224
|
atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=QZg-R2iTQt7kFb8wNtnTmwraSGwvUs34JIasdbNa7ZU,5154
|
|
224
225
|
atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -254,8 +255,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
|
|
|
254
255
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
255
256
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
256
257
|
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.
|
|
258
|
+
atomicshop-2.12.23.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
259
|
+
atomicshop-2.12.23.dist-info/METADATA,sha256=mDSQpG6SXbiytprFFrq70833uv_No7bDq5EtU_2q3xw,10479
|
|
260
|
+
atomicshop-2.12.23.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
261
|
+
atomicshop-2.12.23.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
262
|
+
atomicshop-2.12.23.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|