atomicshop 2.11.47__py3-none-any.whl → 3.10.5__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.
Files changed (268) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
  4. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  5. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  6. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  7. atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
  8. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  9. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  10. atomicshop/a_mains/github_wrapper.py +11 -0
  11. atomicshop/a_mains/install_ca_certificate.py +172 -0
  12. atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
  13. atomicshop/a_mains/process_from_port.py +119 -0
  14. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  15. atomicshop/a_mains/update_config_toml.py +38 -0
  16. atomicshop/appointment_management.py +5 -3
  17. atomicshop/basics/ansi_escape_codes.py +3 -1
  18. atomicshop/basics/argparse_template.py +2 -0
  19. atomicshop/basics/booleans.py +27 -30
  20. atomicshop/basics/bytes_arrays.py +43 -0
  21. atomicshop/basics/classes.py +149 -1
  22. atomicshop/basics/dicts.py +12 -0
  23. atomicshop/basics/enums.py +2 -2
  24. atomicshop/basics/exceptions.py +5 -1
  25. atomicshop/basics/list_of_classes.py +29 -0
  26. atomicshop/basics/list_of_dicts.py +69 -5
  27. atomicshop/basics/lists.py +14 -0
  28. atomicshop/basics/multiprocesses.py +374 -50
  29. atomicshop/basics/package_module.py +10 -0
  30. atomicshop/basics/strings.py +160 -7
  31. atomicshop/basics/threads.py +14 -0
  32. atomicshop/basics/tracebacks.py +13 -4
  33. atomicshop/certificates.py +153 -52
  34. atomicshop/config_init.py +12 -7
  35. atomicshop/console_user_response.py +7 -14
  36. atomicshop/consoles.py +9 -0
  37. atomicshop/datetimes.py +98 -0
  38. atomicshop/diff_check.py +340 -40
  39. atomicshop/dns.py +128 -12
  40. atomicshop/etws/_pywintrace_fix.py +17 -0
  41. atomicshop/etws/const.py +38 -0
  42. atomicshop/etws/providers.py +21 -0
  43. atomicshop/etws/sessions.py +43 -0
  44. atomicshop/etws/trace.py +168 -0
  45. atomicshop/etws/traces/trace_dns.py +162 -0
  46. atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
  47. atomicshop/etws/traces/trace_tcp.py +130 -0
  48. atomicshop/file_io/csvs.py +222 -24
  49. atomicshop/file_io/docxs.py +35 -18
  50. atomicshop/file_io/file_io.py +35 -19
  51. atomicshop/file_io/jsons.py +49 -0
  52. atomicshop/file_io/tomls.py +139 -0
  53. atomicshop/filesystem.py +864 -293
  54. atomicshop/get_process_list.py +133 -0
  55. atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
  56. atomicshop/http_parse.py +149 -93
  57. atomicshop/ip_addresses.py +6 -1
  58. atomicshop/mitm/centered_settings.py +132 -0
  59. atomicshop/mitm/config_static.py +207 -0
  60. atomicshop/mitm/config_toml_editor.py +55 -0
  61. atomicshop/mitm/connection_thread_worker.py +875 -357
  62. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  63. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  64. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  65. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  66. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  67. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  68. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  69. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  70. atomicshop/mitm/engines/create_module_template.py +58 -14
  71. atomicshop/mitm/import_config.py +359 -139
  72. atomicshop/mitm/initialize_engines.py +160 -74
  73. atomicshop/mitm/message.py +64 -23
  74. atomicshop/mitm/mitm_main.py +892 -0
  75. atomicshop/mitm/recs_files.py +183 -0
  76. atomicshop/mitm/shared_functions.py +4 -10
  77. atomicshop/mitm/ssh_tester.py +82 -0
  78. atomicshop/mitm/statistic_analyzer.py +257 -166
  79. atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
  80. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
  81. atomicshop/monitor/change_monitor.py +96 -120
  82. atomicshop/monitor/checks/dns.py +139 -70
  83. atomicshop/monitor/checks/file.py +77 -0
  84. atomicshop/monitor/checks/network.py +81 -77
  85. atomicshop/monitor/checks/process_running.py +33 -34
  86. atomicshop/monitor/checks/url.py +94 -0
  87. atomicshop/networks.py +671 -0
  88. atomicshop/on_exit.py +205 -0
  89. atomicshop/package_mains_processor.py +84 -0
  90. atomicshop/permissions/permissions.py +22 -0
  91. atomicshop/permissions/ubuntu_permissions.py +239 -0
  92. atomicshop/permissions/win_permissions.py +33 -0
  93. atomicshop/print_api.py +24 -41
  94. atomicshop/process.py +63 -17
  95. atomicshop/process_poller/__init__.py +0 -0
  96. atomicshop/process_poller/pollers/__init__.py +0 -0
  97. atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
  98. atomicshop/process_poller/process_pool.py +207 -0
  99. atomicshop/process_poller/simple_process_pool.py +311 -0
  100. atomicshop/process_poller/tracer_base.py +45 -0
  101. atomicshop/process_poller/tracers/__init__.py +0 -0
  102. atomicshop/process_poller/tracers/event_log.py +46 -0
  103. atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
  104. atomicshop/python_file_patcher.py +1 -1
  105. atomicshop/python_functions.py +27 -75
  106. atomicshop/question_answer_engine.py +2 -2
  107. atomicshop/scheduling.py +24 -5
  108. atomicshop/sound.py +4 -2
  109. atomicshop/speech_recognize.py +8 -0
  110. atomicshop/ssh_remote.py +158 -172
  111. atomicshop/startup/__init__.py +0 -0
  112. atomicshop/startup/win/__init__.py +0 -0
  113. atomicshop/startup/win/startup_folder.py +53 -0
  114. atomicshop/startup/win/task_scheduler.py +119 -0
  115. atomicshop/system_resource_monitor.py +61 -46
  116. atomicshop/system_resources.py +8 -8
  117. atomicshop/tempfiles.py +1 -2
  118. atomicshop/timer.py +30 -11
  119. atomicshop/urls.py +41 -0
  120. atomicshop/venvs.py +28 -0
  121. atomicshop/versioning.py +27 -0
  122. atomicshop/web.py +110 -25
  123. atomicshop/web_apis/__init__.py +0 -0
  124. atomicshop/web_apis/google_custom_search.py +44 -0
  125. atomicshop/web_apis/google_llm.py +188 -0
  126. atomicshop/websocket_parse.py +450 -0
  127. atomicshop/wrappers/certauthw/certauth.py +1 -0
  128. atomicshop/wrappers/cryptographyw.py +29 -8
  129. atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
  130. atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
  131. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
  132. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  133. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  134. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
  135. atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
  136. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  137. atomicshop/wrappers/ctyping/win_console.py +39 -0
  138. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  139. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  140. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  141. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  142. atomicshop/wrappers/factw/get_file_data.py +12 -5
  143. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  144. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  145. atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
  146. atomicshop/wrappers/githubw.py +583 -51
  147. atomicshop/wrappers/loggingw/consts.py +49 -0
  148. atomicshop/wrappers/loggingw/filters.py +102 -0
  149. atomicshop/wrappers/loggingw/formatters.py +58 -71
  150. atomicshop/wrappers/loggingw/handlers.py +459 -40
  151. atomicshop/wrappers/loggingw/loggers.py +19 -0
  152. atomicshop/wrappers/loggingw/loggingw.py +1010 -178
  153. atomicshop/wrappers/loggingw/reading.py +344 -19
  154. atomicshop/wrappers/mongodbw/__init__.py +0 -0
  155. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  156. atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
  157. atomicshop/wrappers/netshw.py +271 -0
  158. atomicshop/wrappers/playwrightw/engine.py +34 -19
  159. atomicshop/wrappers/playwrightw/infra.py +5 -0
  160. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  161. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  162. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  163. atomicshop/wrappers/playwrightw/waits.py +9 -7
  164. atomicshop/wrappers/powershell_networking.py +80 -0
  165. atomicshop/wrappers/psutilw/processes.py +81 -0
  166. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  167. atomicshop/wrappers/psutilw/psutilw.py +9 -0
  168. atomicshop/wrappers/pyopensslw.py +9 -2
  169. atomicshop/wrappers/pywin32w/__init__.py +0 -0
  170. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  171. atomicshop/wrappers/pywin32w/console.py +34 -0
  172. atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
  173. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  174. atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
  175. atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
  176. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
  177. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
  178. atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
  179. atomicshop/wrappers/pywin32w/winshell.py +19 -0
  180. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  181. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  182. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  183. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  184. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  185. atomicshop/wrappers/socketw/accepter.py +21 -7
  186. atomicshop/wrappers/socketw/certificator.py +216 -150
  187. atomicshop/wrappers/socketw/creator.py +190 -50
  188. atomicshop/wrappers/socketw/dns_server.py +500 -173
  189. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  190. atomicshop/wrappers/socketw/process_getter.py +86 -0
  191. atomicshop/wrappers/socketw/receiver.py +144 -102
  192. atomicshop/wrappers/socketw/sender.py +65 -35
  193. atomicshop/wrappers/socketw/sni.py +334 -165
  194. atomicshop/wrappers/socketw/socket_base.py +134 -0
  195. atomicshop/wrappers/socketw/socket_client.py +137 -95
  196. atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
  197. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  198. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  199. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  200. atomicshop/wrappers/sysmonw.py +157 -0
  201. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  202. atomicshop/wrappers/win_auditw.py +189 -0
  203. atomicshop/wrappers/winregw/__init__.py +0 -0
  204. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  205. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  206. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
  207. atomicshop-3.10.5.dist-info/RECORD +306 -0
  208. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  209. atomicshop/_basics_temp.py +0 -101
  210. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  211. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  212. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  213. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  214. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  215. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  216. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  217. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  218. atomicshop/addons/package_setup/Setup.cmd +0 -7
  219. atomicshop/addons/process_list/compile.cmd +0 -2
  220. atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  221. atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  222. atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  223. atomicshop/archiver/_search_in_zip.py +0 -189
  224. atomicshop/archiver/archiver.py +0 -34
  225. atomicshop/archiver/search_in_archive.py +0 -250
  226. atomicshop/archiver/sevenz_app_w.py +0 -86
  227. atomicshop/archiver/sevenzs.py +0 -44
  228. atomicshop/archiver/zips.py +0 -293
  229. atomicshop/etw/dns_trace.py +0 -118
  230. atomicshop/etw/etw.py +0 -61
  231. atomicshop/file_types.py +0 -24
  232. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  233. atomicshop/mitm/initialize_mitm_server.py +0 -240
  234. atomicshop/monitor/checks/hash.py +0 -44
  235. atomicshop/monitor/checks/hash_checks/file.py +0 -55
  236. atomicshop/monitor/checks/hash_checks/url.py +0 -62
  237. atomicshop/pbtkmultifile_argparse.py +0 -88
  238. atomicshop/permissions.py +0 -110
  239. atomicshop/process_poller.py +0 -237
  240. atomicshop/script_as_string_processor.py +0 -38
  241. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  242. atomicshop/ssh_scripts/process_from_port.py +0 -27
  243. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  244. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  245. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  246. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  247. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  248. atomicshop/wrappers/ffmpegw.py +0 -125
  249. atomicshop/wrappers/loggingw/checks.py +0 -20
  250. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  251. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  252. atomicshop/wrappers/socketw/base.py +0 -59
  253. atomicshop/wrappers/socketw/get_process.py +0 -107
  254. atomicshop/wrappers/wslw.py +0 -191
  255. atomicshop-2.11.47.dist-info/RECORD +0 -251
  256. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  257. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  258. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  259. /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  260. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  261. /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
  262. /atomicshop/{archiver → etws}/__init__.py +0 -0
  263. /atomicshop/{etw → etws/traces}/__init__.py +0 -0
  264. /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
  265. /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
  266. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  267. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  268. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,311 @@
1
+ import threading
2
+ from pathlib import Path
3
+ import time
4
+ import multiprocessing.managers
5
+ import queue
6
+
7
+
8
+ from ..wrappers.pywin32w.win_event_log.subscribes import process_create, process_terminate
9
+ from .. import get_process_list
10
+ from ..print_api import print_api
11
+
12
+
13
+ WAIT_BEFORE_PROCESS_TERMINATION_CHECK_SECONDS: float = 3
14
+ WAIT_BEFORE_PROCESS_TERMINATION_CHECK_COUNTS: float = WAIT_BEFORE_PROCESS_TERMINATION_CHECK_SECONDS * 10
15
+
16
+ WAIT_FOR_PROCESS_POLLER_PID_SECONDS: int = 3
17
+ WAIT_FOR_PROCESS_POLLER_PID_COUNTS: int = WAIT_FOR_PROCESS_POLLER_PID_SECONDS * 10
18
+
19
+
20
+ class PidProcessConverterPIDNotFoundError(Exception):
21
+ pass
22
+
23
+
24
+ class SimpleProcessPool:
25
+ """
26
+ THE POOL OF PROCESSES IS NOT REAL TIME!!!
27
+ There can be several moments delay (less than a second) + you can add delay before pid removal from the pool.
28
+ This is needed only to correlate PIDs to process names and command lines of other events you get on Windows.
29
+ The idea is similar to the process_poller.process_pool.ProcessPool class, but this class is simpler and uses
30
+ only the pywin32 tracing of the Windows Event Log Process Creation and Process Termination events.
31
+ The simple process pool is used to get things simpler than the process_pool.ProcessPool class.
32
+
33
+ Example of starting the process pool in multiprocess:
34
+ import sys
35
+
36
+ from atomicshop.process_poller import simple_process_pool
37
+
38
+
39
+ def start_process_pool(process_pool_shared_dict_proxy):
40
+ process_poller = simple_process_pool.SimpleProcessPool(
41
+ process_pool_shared_dict_proxy=process_pool_shared_dict_proxy)
42
+ process_poller.start()
43
+
44
+ try:
45
+ # Keep the process alive.
46
+ while True:
47
+ time.sleep(1)
48
+ except KeyboardInterrupt:
49
+ process_poller.stop()
50
+
51
+
52
+ def main():
53
+ # Create the shared multiprocessing dictionary of the process pool.
54
+ manager = multiprocessing.Manager()
55
+ multiprocess_dict_proxy = manager.dict()
56
+
57
+ # Start the process pool in a separate process.
58
+ pool_process = multiprocessing.Process(target=start_process_pool, args=(multiprocess_dict_proxy,))
59
+ pool_process.start()
60
+
61
+ # Pass the shared dict proxy to other functions.
62
+
63
+
64
+ if __name__ == '__main__':
65
+ sys.exit(main())
66
+ """
67
+
68
+ def __init__(
69
+ self,
70
+ wait_before_pid_remove_seconds: float = 5,
71
+ process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy = None
72
+ ):
73
+ """
74
+ :param wait_before_pid_remove_seconds: float, how many seconds to wait before the process is removed from
75
+ the pool after process termination event is received for that pid.
76
+ :param process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy, shared dict proxy to update
77
+ the process pool.
78
+ If you run a function from other multiprocessing.Process, you can pass the shared_dict_proxy to the function
79
+ and update the process pool from that function.
80
+
81
+ Example:
82
+ import multiprocessing.managers
83
+
84
+ manager: multiprocessing.managers.SyncManager = multiprocessing.Manager()
85
+ multiprocess_dict_proxy: multiprocessing.managers.DictProxy = manager.dict()
86
+
87
+ process_poller = SimpleProcessPool()
88
+ process_poller.start()
89
+
90
+ while True:
91
+ #============================
92
+ # your function where you get info with pid
93
+ # result = {
94
+ # 'pid': 1234,
95
+ # 'info': 'some info'
96
+ # }
97
+ #============================
98
+ info_with_process_details = {
99
+ 'pid': result['pid'],
100
+ 'info': result['info']
101
+ 'process_details': shared_dict_proxy[result['pid']]
102
+ }
103
+
104
+ break
105
+
106
+ process_poller.stop()
107
+ manager.shutdown()
108
+ """
109
+
110
+ self.wait_before_pid_remove_seconds: float = wait_before_pid_remove_seconds
111
+ self.process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy = process_pool_shared_dict_proxy
112
+
113
+ self._processes: dict = dict()
114
+ self._running: bool = False
115
+ self.shared_dict_update_queue: queue.Queue = queue.Queue()
116
+ self.empty_cmdline_queue: queue.Queue = queue.Queue()
117
+
118
+ def start(self):
119
+ self._running = True
120
+
121
+ self._processes = get_process_list.GetProcessList(
122
+ get_method='psutil', connect_on_init=True).get_processes(as_dict=True)
123
+
124
+ thread_get_queue = threading.Thread(target=self._start_main_thread, args=(self.shared_dict_update_queue,))
125
+ thread_get_queue.daemon = True
126
+ thread_get_queue.start()
127
+
128
+ thread_process_termination = threading.Thread(target=self._thread_process_termination)
129
+ thread_process_termination.daemon = True
130
+ thread_process_termination.start()
131
+
132
+ thread_get_psutil_commandline = threading.Thread(target=self._thread_get_psutil_commandline)
133
+ thread_get_psutil_commandline.daemon = True
134
+ thread_get_psutil_commandline.start()
135
+
136
+ thread_update_shared_dict = threading.Thread(target=self._update_shared_dict, args=(self.shared_dict_update_queue,))
137
+ thread_update_shared_dict.daemon = True
138
+ thread_update_shared_dict.start()
139
+
140
+ def stop(self):
141
+ self._running = False
142
+
143
+ def get_processes(self):
144
+ return self._processes
145
+
146
+ def _start_main_thread(self, shared_dict_update_queue):
147
+ get_instance = process_create.ProcessCreateSubscriber()
148
+ get_instance.start()
149
+
150
+ while self._running:
151
+ event = get_instance.emit()
152
+ process_id = event['NewProcessIdInt']
153
+ process_name = Path(event['NewProcessName']).name
154
+ command_line = event['CommandLine']
155
+
156
+ # The event log tracing method doesn't always give the command line, unlike the psutil method.
157
+ # So, we'll get the command line from the current psutil snapshot separately.
158
+ if command_line == '':
159
+ self.empty_cmdline_queue.put(process_id)
160
+
161
+ self._processes[process_id] = {
162
+ 'name': process_name,
163
+ 'cmdline': command_line
164
+ }
165
+
166
+ # Update the multiprocessing shared dict proxy.
167
+ shared_dict_update_queue.put(dict(self._processes))
168
+
169
+ def _thread_get_psutil_commandline(self):
170
+ """
171
+ This function will get an entry from the queue where command line is missing and get the command line
172
+ from the psutil snapshot.
173
+ """
174
+
175
+ while self._running:
176
+ empty_cmd_pid = self.empty_cmdline_queue.get()
177
+ current_psutil_process_snapshot: dict = get_process_list.GetProcessList(
178
+ get_method='psutil', connect_on_init=True).get_processes(as_dict=True)
179
+ command_line = current_psutil_process_snapshot[empty_cmd_pid]['cmdline']
180
+
181
+ self._processes[empty_cmd_pid]['cmdline'] = command_line
182
+
183
+ # Update the multiprocessing shared dict proxy.
184
+ self.shared_dict_update_queue.put(dict(self._processes))
185
+
186
+ def _thread_process_termination(self):
187
+ process_terminate_instance = process_terminate.ProcessTerminateSubscriber()
188
+ process_terminate_instance.start()
189
+
190
+ while self._running:
191
+ termination_event = process_terminate_instance.emit()
192
+ process_id = termination_event['ProcessIdInt']
193
+
194
+ removal_thread = threading.Thread(target=self._remove_pid, args=(process_id,))
195
+ removal_thread.daemon = True
196
+ removal_thread.start()
197
+
198
+ def _remove_pid(self, process_id):
199
+ # We need to wait a bit before we remove the process.
200
+ # This is because termination event can come sooner than the creation and the process
201
+ # is not yet in the pool.
202
+ # This happens mostly when the process is terminated immediately after the creation.
203
+ # Example: ping example.c
204
+ # 'example.c' is not a valid address, so the process is terminated immediately after the creation.
205
+ counter = 0
206
+ while counter < WAIT_BEFORE_PROCESS_TERMINATION_CHECK_COUNTS:
207
+ if process_id in self._processes:
208
+ break
209
+ counter += 1
210
+ time.sleep(0.1)
211
+
212
+ if counter == WAIT_BEFORE_PROCESS_TERMINATION_CHECK_COUNTS:
213
+ # print_api(f'Process [{process_id}] not found in the pool.', color='yellow')
214
+ return
215
+
216
+ # time.sleep(1)
217
+ # if process_id not in self._processes:
218
+ # print_api(f'Process [{process_id}] not found in the pool.', color='red')
219
+ # return
220
+
221
+ time.sleep(self.wait_before_pid_remove_seconds)
222
+ _ = self._processes.pop(process_id, None)
223
+ # print_api(f'Process [{process_id}] removed from the pool.', color='yellow')
224
+
225
+ self.shared_dict_update_queue.put(dict(self._processes))
226
+
227
+ def _update_shared_dict(self, shared_dict_update_queue):
228
+ while self._running:
229
+ current_process_pool = shared_dict_update_queue.get()
230
+ if self.process_pool_shared_dict_proxy is not None:
231
+ self.process_pool_shared_dict_proxy.clear()
232
+ self.process_pool_shared_dict_proxy.update(current_process_pool)
233
+
234
+
235
+ class PidProcessConverter:
236
+ """
237
+ This class is used to get the process details by PID from the process pool shared dict proxy.
238
+ """
239
+
240
+ def __init__(
241
+ self,
242
+ process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy
243
+ ):
244
+ """
245
+ :param process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy, multiprocessing shared dict proxy.
246
+ """
247
+
248
+ self.process_pool_shared_dict_proxy: multiprocessing.managers.DictProxy = process_pool_shared_dict_proxy
249
+
250
+ self.get_process_with_psutil = get_process_list.GetProcessList(get_method='psutil', connect_on_init=True)
251
+
252
+ def get_process_by_pid(self, pid: int):
253
+ """
254
+ THIS FUNCTION WILL RUN OUTSIDE THE PROCESS POOL PROCESS. Inside the function that needs
255
+ the pid to process conversion.
256
+ Get the process details by PID from the process pool shared dict proxy.
257
+
258
+ :param pid: int, the process ID.
259
+ :return: dict, the process details.
260
+ """
261
+
262
+ counter = 0
263
+ process_dict: dict = dict()
264
+ while counter < WAIT_FOR_PROCESS_POLLER_PID_COUNTS:
265
+ # We need it so that the pool will not change in the middle of the process.
266
+ current_pid_pool = convert_proxy_dict_to_dict(self.process_pool_shared_dict_proxy)
267
+ if pid not in current_pid_pool:
268
+ # print(dict(self.process_pool_shared_dict_proxy))
269
+ time.sleep(0.1)
270
+ counter += 1
271
+ else:
272
+ process_dict = current_pid_pool[pid]
273
+ break
274
+
275
+ if not process_dict:
276
+ print_api(f"Error: The PID [{pid}] is not in the pool, trying psutil snapshot.", color='yellow')
277
+ # Last resort, try to get the process name by current process snapshot.
278
+ processes = self.get_process_with_psutil.get_processes(as_dict=True)
279
+ if pid not in processes:
280
+ print_api(f"Error: Couldn't get the process name for PID: {pid}.", color='red')
281
+ process_dict = {
282
+ 'name': pid,
283
+ 'cmdline': ''
284
+ }
285
+ else:
286
+ process_dict = processes[pid]
287
+
288
+ return process_dict
289
+
290
+
291
+ def convert_proxy_dict_to_dict(proxy_dict: multiprocessing.managers.DictProxy) -> dict:
292
+ """
293
+ Convert the multiprocessing shared dict proxy to a normal dict.
294
+
295
+ :param proxy_dict: multiprocessing.managers.DictProxy, the shared dict proxy.
296
+ :return: dict, the normal dict.
297
+ """
298
+
299
+ # Create a snapshot of the keys
300
+ keys = list(proxy_dict.keys())
301
+ current_pid_pool = {}
302
+
303
+ for key in keys:
304
+ try:
305
+ # Attempt to retrieve the value for each key
306
+ current_pid_pool[key] = proxy_dict[key]
307
+ except KeyError:
308
+ # The key was removed concurrently; skip it
309
+ continue
310
+
311
+ return current_pid_pool
@@ -0,0 +1,45 @@
1
+ import threading
2
+
3
+ from .. import get_process_list
4
+
5
+
6
+ class Tracer:
7
+ """
8
+ Main tracer class for getting the list of processes.
9
+ """
10
+
11
+ def __init__(self):
12
+ self.tracer_instance = None
13
+ self.process_queue = None
14
+
15
+ self._cycle_callable = None
16
+ self._processes = {}
17
+
18
+ def start(self):
19
+ """
20
+ Start the tracer.
21
+ """
22
+
23
+ self._processes = get_process_list.GetProcessList(
24
+ get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
25
+
26
+ self.process_queue.put(self._processes)
27
+
28
+ self.tracer_instance.start()
29
+
30
+ thread = threading.Thread(target=self.update_queue)
31
+ thread.daemon = True
32
+ thread.start()
33
+
34
+ def update_queue(self):
35
+ """
36
+ Update the list of processes.
37
+ """
38
+
39
+ while True:
40
+ # Get subclass specific get process cycle.
41
+ current_processes = self._cycle_callable()
42
+
43
+ self._processes.update(current_processes)
44
+
45
+ self.process_queue.put(self._processes)
File without changes
@@ -0,0 +1,46 @@
1
+ from pathlib import Path
2
+
3
+ from ...wrappers.pywin32w.win_event_log.subscribes import process_create, process_terminate
4
+ from .. import tracer_base
5
+
6
+
7
+ class TracerEventlog(tracer_base.Tracer):
8
+ """
9
+ Tracer subclass for getting the list of processes with Windows Event Log Subscription.
10
+ """
11
+ def __init__(
12
+ self,
13
+ process_queue
14
+ ):
15
+ """
16
+ :param process_queue: Queue. The queue to put the processes in. If None, the processes will not be put in the
17
+ queue.
18
+ """
19
+ super().__init__()
20
+
21
+ self.tracer_instance = process_create.ProcessCreateSubscriber()
22
+
23
+ self.process_queue = process_queue
24
+
25
+ # This function is used in the thread start in the main Tracer class, 'start' function.
26
+ self._cycle_callable = self.emit_cycle
27
+
28
+ def start(self):
29
+ """
30
+ Start the tracer.
31
+ """
32
+ super().start()
33
+
34
+ def emit_cycle(self):
35
+ """
36
+ Get the list of processes.
37
+ """
38
+
39
+ # Get the current processes and reinitialize the instance of the dict.
40
+ current_cycle: dict = self.tracer_instance.emit()
41
+ current_processes: dict = {int(current_cycle['NewProcessIdInt']): {
42
+ 'name': Path(current_cycle['NewProcessName']).name,
43
+ 'cmdline': current_cycle['CommandLine']}
44
+ }
45
+
46
+ return current_processes
@@ -0,0 +1,68 @@
1
+ from .. import tracer_base
2
+ from ...etws.traces import trace_sysmon_process_creation
3
+
4
+
5
+ class TracerSysmonEtw(tracer_base.Tracer):
6
+ """
7
+ Tracer subclass for getting the list of processes with SysMon ETW.
8
+ """
9
+ def __init__(
10
+ self,
11
+ attrs: list = None,
12
+ settings: dict = None,
13
+ process_queue=None
14
+ ):
15
+ """
16
+ :param attrs: list. Default is ['pid', 'original_file_name', 'command_line'].
17
+ The list of attributes to get from the sysmon trace output. Check SysmonProcessCreationTrace class for
18
+ available attributes.
19
+ :settings: dict. The settings dictionary:
20
+ 'sysmon_etw_session_name': str. The Sysmon ETW session name. If None, the default from
21
+ 'trace_sysmon_process_creation' will be used.
22
+ 'sysmon_directory': str. The directory where Sysmon.exe resides. If None, the default from
23
+ 'trace_sysmon_process_creation' will be used.
24
+ :param process_queue: Queue. The queue to put the processes in. If None, the processes will not be put in the
25
+ queue.
26
+ """
27
+ super().__init__()
28
+
29
+ if not attrs:
30
+ attrs = ['ProcessId', 'OriginalFileName', 'CommandLine']
31
+
32
+ if not settings:
33
+ settings = {
34
+ 'sysmon_etw_session_name': None,
35
+ 'sysmon_directory': None
36
+ }
37
+
38
+ self.tracer_instance = trace_sysmon_process_creation.SysmonProcessCreationTrace(
39
+ attrs=attrs,
40
+ session_name=settings.get('sysmon_etw_session_name'),
41
+ close_existing_session_name=True,
42
+ sysmon_directory=settings.get('sysmon_directory')
43
+ )
44
+
45
+ self.process_queue = process_queue
46
+
47
+ # This function is used in the thread start in the main Tracer class, 'start' function.
48
+ self._cycle_callable = self.emit_cycle
49
+
50
+ def start(self):
51
+ """
52
+ Start the tracer.
53
+ """
54
+ super().start()
55
+
56
+ def emit_cycle(self):
57
+ """
58
+ Get the list of processes.
59
+ """
60
+
61
+ # Get the current processes and reinitialize the instance of the dict.
62
+ current_cycle: dict = self.tracer_instance.emit()
63
+ current_processes: dict = {int(current_cycle['ProcessId']): {
64
+ 'name': current_cycle['OriginalFileName'],
65
+ 'cmdline': current_cycle['CommandLine']}
66
+ }
67
+
68
+ return current_processes
@@ -113,4 +113,4 @@ def find_and_replace_in_file(
113
113
  for index in found_string_indexes:
114
114
  file_data[index] = file_data[index].replace(single_find.find_what, single_find.replace_to)
115
115
 
116
- file_io.write_file(content=file_data, file_path=file_path, encoding=encoding, convert_list_to_string=True)
116
+ file_io.write_file(content=file_data, file_path=file_path, encoding=encoding)
@@ -1,9 +1,10 @@
1
1
  import sys
2
+ from typing import Union
2
3
 
3
4
  from .print_api import print_api
4
5
 
5
6
 
6
- def get_current_python_version_string() -> str:
7
+ def get_python_version_string() -> str:
7
8
  """
8
9
  Function gets version MAJOR.MINOR.MICRO from 'sys.version_info' object and returns it as a string.
9
10
  :return: python MAJOR.MINOR.MICRO version string.
@@ -12,84 +13,35 @@ def get_current_python_version_string() -> str:
12
13
  return f'{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}'
13
14
 
14
15
 
15
- # noinspection PyUnusedLocal
16
- def check_if_version_object_is_tuple_or_string(version_object: any,
17
- **kwargs) -> tuple:
18
- """
19
- Function checks if 'version_object' that was passed is tuple or string.
20
- If it's tuple then returns it back. If it's string, converts to tuple then returns it.
21
- If the object is none of the above, returns None.
22
-
23
- :param version_object: Can be string ('3.10') or tuple of integers ((3, 10)).
24
- :return:
25
- """
26
- # Check if tuple was passed.
27
- if isinstance(version_object, tuple):
28
- return version_object
29
- else:
30
- # Then check if a string was passed.
31
- if isinstance(version_object, str):
32
- # The check will be against tuple of integers, so we'll convert a string to tuple of integers.
33
- return tuple(map(int, version_object.split('.')))
34
- else:
35
- message = f'[*] Function: [check_if_version_object_is_tuple_or_string]\n' \
36
- f'[*] [version_object] object passed is not tuple or string.\n' \
37
- f'[*] Object type: {type(version_object)}\n' \
38
- f'[*] Object content {version_object}\n' \
39
- f'Exiting...'
40
- print_api(message, error_type=True, logger_method='critical', **kwargs)
41
-
42
- return None
43
-
44
-
45
- # noinspection PyUnusedLocal
46
- def check_python_version_compliance(minimum_version: any,
47
- maximum_version: any = None,
48
- **kwargs) -> bool:
16
+ def check_python_version_compliance(
17
+ min_ver: tuple = None,
18
+ max_ver: tuple = None,
19
+ ) -> str | None:
49
20
  """
50
21
  Python version check. Should be executed before importing external libraries, since they depend on Python version.
51
22
 
52
- :param minimum_version: Can be string ('3.10') or tuple of integers ((3, 10)).
53
- :param maximum_version: Can be string ('3.10') or tuple of integers ((3, 10)).
23
+ :param min_ver: tuple of integers (3, 10).
24
+ :param max_ver: tuple of integers (3, 10).
54
25
  If maximum version is not specified, it will be considered as all versions above the minimum are compliant.
55
- :return:
26
+ :return: If version is not compliant, returns string with error message. Otherwise, returns None.
56
27
  """
57
28
 
58
- # Check 'minimum_version' object for string or tuple and get the tuple.
59
- minimum_version_scheme: tuple = check_if_version_object_is_tuple_or_string(minimum_version, **kwargs)
60
- # If 'maximum_version' object was passed, check it for string or tuple and get the tuple.
61
- if maximum_version:
62
- maximum_version_scheme: tuple = check_if_version_object_is_tuple_or_string(maximum_version, **kwargs)
63
-
64
- # Get current python version.
65
- python_version_full: str = get_current_python_version_string()
66
-
67
- message = f"[*] Current Python Version: {python_version_full}"
68
- print_api(message, logger_method='info', **kwargs)
69
-
70
- # if 'maximum_version' passed and current python version is later or equals to the minimum and earlier than
71
- # maximum version required.
72
- if maximum_version:
73
- if not sys.version_info >= minimum_version_scheme or not sys.version_info < maximum_version_scheme:
74
- message = f"[!!!] YOU NEED TO INSTALL AT LEAST PYTHON " \
75
- f"{'.'.join(str(i) for i in minimum_version_scheme)}, " \
76
- f"AND EARLIER THAN {'.'.join(str(i) for i in maximum_version_scheme)}, " \
77
- f"to work properly. Unhandled exceptions are inevitable!"
78
- print_api(message, error_type=True, logger_method='critical', **kwargs)
79
-
80
- return False
81
- # If 'maximum_version' wasn't passed.
29
+ if not min_ver and not max_ver:
30
+ raise ValueError("At least one of the version parameters should be passed.")
31
+
32
+ current_version_info: tuple = sys.version_info[:3]
33
+ if min_ver and not max_ver:
34
+ if current_version_info < min_ver:
35
+ return f'Python version {".".join(map(str, min_ver))} or higher is required. '\
36
+ f'Current version is {".".join(map(str, current_version_info))}.'
37
+ elif max_ver and not min_ver:
38
+ if current_version_info > max_ver:
39
+ return f'Python version up to {".".join(map(str, max_ver))} is required. '\
40
+ f'Current version is {".".join(map(str, current_version_info))}.'
41
+ elif min_ver and max_ver:
42
+ if not (min_ver <= current_version_info <= max_ver):
43
+ return f'Python version between {".".join(map(str, min_ver))} and '\
44
+ f'{".".join(map(str, max_ver))} is required. '\
45
+ f'Current version is {".".join(map(str, current_version_info))}.'
82
46
  else:
83
- # Check if current python version is later or equals to the minimum required version.
84
- if not sys.version_info >= minimum_version_scheme:
85
- message = f"[!!!] YOU NEED TO INSTALL AT LEAST PYTHON " \
86
- f"{'.'.join(str(i) for i in minimum_version_scheme)}, " \
87
- f"to work properly. Unhandled exceptions are inevitable!"
88
- print_api(message, error_type=True, logger_method='critical', **kwargs)
89
-
90
- return False
91
-
92
- message = "[*] Version Check PASSED."
93
- print_api(message, logger_method='info', **kwargs)
94
-
95
- return True
47
+ return None
@@ -4,7 +4,7 @@ import sys
4
4
  import json
5
5
 
6
6
  # Custom class imports.
7
- from atomicshop.filesystem import check_file_existence
7
+ from atomicshop import filesystem
8
8
 
9
9
 
10
10
  class QAEngine:
@@ -20,7 +20,7 @@ class QAEngine:
20
20
  # Get 'qa.json' full path.
21
21
  qa_fullpath: str = script_directory + os.sep + self.qa_filename
22
22
  # Check if it exists.
23
- if not check_file_existence(qa_fullpath):
23
+ if not filesystem.is_file_exists(qa_fullpath):
24
24
  print(f'File non-existent: {qa_fullpath}')
25
25
  sys.exit()
26
26