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 CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.12.22'
4
+ __version__ = '2.12.24'
@@ -1,2 +1,7 @@
1
- "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\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
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
@@ -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)
@@ -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 etw
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 = etw.EventTrace(
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
- event_dict = psutilw.cross_single_connection_with_processes(event_dict, self.process_poller.processes)
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': 'check_object_list' must contain strings of full path to the file.
48
- 'dns': 'check_object_list' will be none, since the DNS events will be queried from the system.
49
- 'network': 'check_object_list' will be none, since the network events will be queried from the system.
50
- 'process_running': 'check_object_list' must contain strings of process names to check if they are 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
- 'url_urllib': 'check_object_list' must contain strings of full URL to a web page. The page will be
54
- downloaded using 'urllib' library in HTML.
55
- 'url_playwright_html': 'check_object_list' must contain strings of full URL to a web page. The page will
56
- be downloaded using 'playwright' library in HTML.
57
- 'url_playwright_pdf': 'check_object_list' must contain strings of full URL to a web page. The page will
58
- be downloaded using 'playwright' library in PDF.
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 store_original_object: boolean, if True, the original object will be stored on the disk inside
88
- 'Original' folder, inside 'input_directory'.
89
- :param operation_type: string, type of operation to perform. The type must be one of the following:
90
- 'hit_statistics': will only store the statistics of the entries in the input file.
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._setup_object()
111
+ self._setup_check()
125
112
 
126
- def _setup_object(self):
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
@@ -1,13 +1,14 @@
1
1
  from pathlib import Path
2
2
  from typing import Union
3
3
 
4
- from ...etw.dns_trace import DnsTrace
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(enable_process_poller=True, attrs=['name', 'cmdline', 'domain', 'query_type']))
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()
@@ -1,19 +1,25 @@
1
- import pkg_resources
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: str = pkg_resources.resource_filename(
13
- __package__, 'addons/process_list/compiled/Win10x64/process_list.dll')
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.WINFUCNTYPE is not thread safe. You should load it inside a thread / process and not outside.
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
- self.dll.GetProcessDetails(self.CALLBACKFUNC(self.callback_output.callback))
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
- processes = self.callback_output.data
75
+ processes = self.callback_output.data
55
76
 
56
- # Clear the callback output list, or it will be appended each time.
57
- self.callback_output.data = list()
77
+ # Clear the callback output list, or it will be appended each time.
78
+ self.callback_output.data = list()
58
79
 
59
- if sort_by:
60
- processes = list_of_dicts.sort_by_keys(processes, key_list=[sort_by])
80
+ if sort_by:
81
+ processes = list_of_dicts.sort_by_keys(processes, key_list=[sort_by])
61
82
 
62
- if as_dict:
63
- processes = convert_processes_to_dict(processes)
83
+ if as_dict:
84
+ processes = convert_processes_to_dict(processes)
64
85
 
65
- return processes
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": process_name.decode("utf-16"),
105
- "cmdline": cmdline.decode("utf-16") if cmdline else "Error"
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": process_name,
111
- "cmdline": cmdline if cmdline else "Error"
143
+ "name": "Error",
144
+ "cmdline": "Error"
112
145
  })
@@ -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).start()
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
- # def _worker(self, process_polling_instance):
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
- # If the list is full (to specified 'store_cycles'), remove the first element.
211
- if len(list_of_processes) == self.store_cycles:
212
- del list_of_processes[0]
213
-
214
- # Get the current processes and reinitialize the instance of the dict.
215
- current_processes: dict = dict(self.get_processes_list.get_processes())
216
-
217
- # Remove Command lines that contains only numbers, since they are useless.
218
- for pid, process_info in current_processes.items():
219
- if process_info['cmdline'].isnumeric():
220
- current_processes[pid]['cmdline'] = str()
221
- elif process_info['cmdline'] == 'Error':
222
- current_processes[pid]['cmdline'] = str()
223
-
224
- # Append the current processes to the list.
225
- list_of_processes.append(current_processes)
226
-
227
- # Merge all dicts in the list to one dict, updating with most recent PIDs.
228
- self.processes = list_of_dicts.merge_to_dict(list_of_processes)
229
-
230
- if self.operation == 'process':
231
- self.queue.put(self.processes)
232
-
233
- time.sleep(self.interval_seconds)
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).start()
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.12.22
3
+ Version: 2.12.24
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=g2vsW13t8sldcuaaZZDrssh2JaK2htDJxU8zZNrBFoM,124
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=TNAK6kQZm5JKWzEW6QLqVHEG98ZLNDQiSS4YwDk8V8c,3830
29
- atomicshop/process_poller.py,sha256=WfmwCLALfTYOq8ri0vkPeqq8ruEyA_43DaN--CU2_XY,10854
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=YiHOIAI7V20bH5qcqf3c1NNkmkLk3EYr3i19CrlTkLw,4214
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=oCXoYNx2RGmI4BfspYhYq9K6fM7D97jQ3WMu4XsNe1Y,281
68
- atomicshop/addons/process_list/process_list.cpp,sha256=e7olpLfLVg9vQnjEr5L2Y8aWGPGpkH39vmz51CaGFcc,5467
69
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll,sha256=SkAZvYAfSbzQTTq-5aL6_dYR2rA4DHbgyenFfgLFzW0,266752
70
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp,sha256=VTph513eqa6f6HmqAj_6mBS1Rf9G56bgYqZNuDePYcs,708
71
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=n9c2MVPs3GBNoOQjMesAwzNpv5aFZsW8c-ADS7GYRhA,1886
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/dns_trace.py,sha256=I4OZsiZUDyj7B4fKTOqsB1tcX1DUMw9uh4CwXlcmHfY,5571
105
- atomicshop/etw/etw.py,sha256=xVJNbfCq4KgRfsDnul6CrIdAMl9xRBixZ-hUyqiB2g4,2403
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=mrLedkgXWxKzQLqjYd3bmD8-or6HlX9sejdXbSvhNMI,9016
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=bPqQACt8IgQ08U-x9G7pAN-iSfxbdOAubJTHZd-lJ84,6792
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.22.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
258
- atomicshop-2.12.22.dist-info/METADATA,sha256=754IBZW-ExlLv33i93QGyMKGQOEXOrw1uIsXmERVvKM,10479
259
- atomicshop-2.12.22.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
260
- atomicshop-2.12.22.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
261
- atomicshop-2.12.22.dist-info/RECORD,,
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