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
atomicshop/print_api.py CHANGED
@@ -1,17 +1,18 @@
1
1
  import sys
2
+ import logging
3
+ from typing import Any
2
4
 
3
- from .basics.ansi_escape_codes import ColorsBasic, get_colors_basic_dict
5
+ from .basics import ansi_escape_codes
4
6
  from .basics import tracebacks
5
7
 
6
8
 
7
- # noinspection PyUnusedLocal,PyIncorrectDocstring
8
9
  def print_api(
9
- message: any,
10
- color: any = None,
10
+ message: Any,
11
+ color: Any = None,
11
12
  print_end: str = '\n',
12
13
  rtl: bool = False,
13
14
  error_type: bool = False,
14
- logger: object = None,
15
+ logger: logging.Logger = None,
15
16
  logger_method: str = 'info',
16
17
  stdout: bool = True,
17
18
  stderr: bool = True,
@@ -19,8 +20,6 @@ def print_api(
19
20
  traceback_string: bool = False,
20
21
  oneline: bool = False,
21
22
  oneline_end: str = '',
22
- # raise_exception: bool = True,
23
- # exit_on_error: bool = False,
24
23
  **kwargs: object) -> None:
25
24
  """
26
25
  Function of custom api that is responsible for printing messages to console.
@@ -82,39 +81,37 @@ def print_api(
82
81
 
83
82
  # This section takes care of different types of string manipulations for message.
84
83
 
85
- # If 'exit_on_error' is set to 'True', we'll add 'exit_message' on new line after 'message'.
86
- # if error_type and exit_on_error and raise_exception:
87
- # message = message + '\n' + exit_message
88
-
89
84
  # If 'rtl' is set to 'True', we'll add Right-To-Left text conversion to 'message'.
90
85
  if rtl:
91
- # Lazy importing of 'bidi' library. It's not a problem since python caches the library after first import.
92
- # Off-course, it will be imported from the cache each time this section is triggered.
93
86
  # pip install python-bidi
94
87
  from bidi.algorithm import get_display
95
88
  message = get_display(message)
96
89
 
90
+ if logger_method == 'error' or logger_method == 'critical':
91
+ error_type = True
92
+
93
+ # If exception was raised and 'stderr=True'.
94
+ if sys.exc_info()[0] is not None and stderr and traceback_string:
95
+ # If 'traceback' is set to 'True', we'll output traceback of exception.
96
+ if traceback_string:
97
+ if message:
98
+ message = f'{message}\n{tracebacks.get_as_string()}{message}'
99
+ else:
100
+ message = tracebacks.get_as_string()
101
+
102
+ color = 'red'
103
+
97
104
  # If 'stdcolor' is 'True', the console output will be colored.
98
105
  if stdcolor:
99
106
  # If 'logger.error' should be outputted to console, and 'color' wasn't selected, then set color to 'yellow'.
100
107
  if logger_method == 'error' and not color:
101
108
  color = 'yellow'
102
- error_type = True
103
109
  # If 'logger.critical' should be outputted to console, and 'color' wasn't selected, then set color to 'red'.
104
110
  elif logger_method == 'critical' and not color:
105
111
  color = 'red'
106
- error_type = True
107
-
108
- if color:
109
- message = get_colors_basic_dict(color) + message + ColorsBasic.END
110
112
 
111
- # If exception was raised and 'stderr=True'.
112
- if sys.exc_info()[0] is not None and stderr:
113
- # If 'traceback' is set to 'True', we'll output traceback of exception.
114
- if traceback_string:
115
- message = f'{message} | Exception: {tracebacks.get_as_string()}'
116
-
117
- color = 'red'
113
+ if color is not None:
114
+ message = ansi_escape_codes.get_colors_basic_dict(color) + message + ansi_escape_codes.ColorsBasic.END
118
115
 
119
116
  # If 'online' is set to 'True', we'll output message as oneline.
120
117
  if oneline:
@@ -129,14 +126,14 @@ def print_api(
129
126
  if print_end == '\n':
130
127
  # Use logger to output message.
131
128
  getattr(logger, logger_method)(message)
129
+ else:
130
+ raise ValueError("Logger can't output messages with 'print_end' other than '\\n'.")
132
131
  # If logger wasn't passed.
133
132
  else:
134
133
  # Use print to output the message.
135
134
  print(message, end=print_end)
136
135
 
137
136
  # = Main Section with printing cases ===============================================================================
138
- # exit_message: str = 'Exiting...'
139
-
140
137
  # Convert message to string.
141
138
  message = str(message)
142
139
 
@@ -150,20 +147,6 @@ def print_api(
150
147
  if error_type:
151
148
  print_or_logger()
152
149
 
153
- # ==================================
154
- # This section is responsible for ending the script.
155
-
156
- # Check if we're inside exception. In this case each of 3 entries in 'sys.exc_info()' tuple will not equal
157
- # to 'None', so picked only the first one.
158
- # if sys.exc_info()[0] and not exit_on_error:
159
- # # If 'raise_exception' is set to 'True', we'll end the script with exception.
160
- # if pass_exception:
161
- # pass
162
-
163
- # If 'exit_on_error' is set to 'True', we'll end the script.
164
- # if exit_on_error and error_type:
165
- # sys.exit()
166
-
167
150
 
168
151
  def print_status(
169
152
  prefix_string: str,
atomicshop/process.py CHANGED
@@ -7,11 +7,16 @@ import shutil
7
7
 
8
8
  from .print_api import print_api
9
9
  from .inspect_wrapper import get_target_function_default_args_and_combine_with_current
10
- from .basics.strings import match_pattern_against_string
10
+ from .basics import strings
11
11
  from .wrappers import ubuntu_terminal
12
+ from .wrappers.psutilw import processes
12
13
 
13
14
  if os.name == 'nt':
14
- from .process_poller import GetProcessList
15
+ from . import get_process_list
16
+
17
+
18
+ class MultipleProcessesFound(Exception):
19
+ pass
15
20
 
16
21
 
17
22
  def is_command_exists(cmd: str) -> bool:
@@ -77,7 +82,18 @@ def execute_with_live_output(
77
82
  :return: Boolean, If execution was successful, return True, if not - False.
78
83
  """
79
84
 
80
- cmd = _execution_parameters_processing(cmd, wsl)
85
+ if isinstance(cmd, str):
86
+ shell = True
87
+ elif isinstance(cmd, list):
88
+ shell = False
89
+ else:
90
+ raise TypeError(f'cmd must be a string or list, not {type(cmd)}')
91
+
92
+ if wsl:
93
+ if isinstance(cmd, str):
94
+ cmd = 'wsl ' + cmd
95
+ elif isinstance(cmd, list):
96
+ cmd = ['wsl'] + cmd
81
97
 
82
98
  # Needed imports:
83
99
  # from subprocess import Popen, PIPE, STDOUT
@@ -98,7 +114,7 @@ def execute_with_live_output(
98
114
  # The buffer size is system-dependent and usually chosen by the underlying implementation to optimize performance.
99
115
  # # bufsize=0: This means no buffering.
100
116
  # The I/O is unbuffered, and data is written or read from the stream immediately.
101
- with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True) as process:
117
+ with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, text=True, shell=shell) as process:
102
118
  # We'll count the number of lines from 'process.stdout'.
103
119
  counter: int = 0
104
120
  # And also get list of all the lines.
@@ -247,44 +263,74 @@ def safe_terminate(popen_process: subprocess.Popen):
247
263
  popen_process.wait()
248
264
 
249
265
 
250
- def match_pattern_against_running_processes_cmdlines(pattern: str, first: bool = False, prefix_suffix: bool = False):
266
+ def get_running_processes_by_cmdline_pattern(
267
+ pattern: str,
268
+ cmdline_case_insensitive: bool = False,
269
+ first: bool = False,
270
+ prefix_suffix: bool = False
271
+ ) -> list:
251
272
  """
252
273
  The function matches specified string pattern including wildcards against all the currently running processes'
253
274
  command lines.
254
275
 
255
276
  :param pattern: string, the pattern that we will search in the command line list of currently running processes.
277
+ :param cmdline_case_insensitive: boolean,
278
+ True, the pattern and the command line will be matched case-insensitive.
256
279
  :param first: boolean, that will set if first pattern match found the iteration will stop, or we will return
257
280
  the list of all command lines that contain the pattern.
258
281
  :param prefix_suffix: boolean. Check the description in 'match_pattern_against_string' function.
282
+
283
+ :return: list, of command lines that contain the pattern.
259
284
  """
260
285
 
261
286
  # Get the list of all the currently running processes.
262
- get_process_list = GetProcessList(get_method='pywin32', connect_on_init=True)
263
- processes = get_process_list.get_processes(as_dict=False)
287
+ get_process_list_instance = get_process_list.GetProcessList(get_method='psutil', connect_on_init=True)
288
+ processes = get_process_list_instance.get_processes(as_dict=False)
264
289
 
265
290
  # Iterate through all the current process, while fetching executable file 'name' and the command line.
266
291
  # Name is always populated, while command line is not.
267
292
  matched_cmdlines: list = list()
268
293
  for process in processes:
269
294
  # Check if command line isn't empty and that string pattern is matched against command line.
270
- if process['cmdline'] and \
271
- match_pattern_against_string(pattern, process['cmdline'], prefix_suffix):
272
- matched_cmdlines.append(process['cmdline'])
273
- # If 'first' was set to 'True' we will stop, since we found the first match.
274
- if first:
275
- break
295
+ if process['cmdline']:
296
+ is_pattern_matched = strings.match_pattern_against_string(
297
+ pattern, process['cmdline'], case_insensitive=cmdline_case_insensitive, prefix_suffix=prefix_suffix)
298
+ if is_pattern_matched:
299
+ matched_cmdlines.append(process)
300
+ # If 'first' was set to 'True' we will stop, since we found the first match.
301
+ if first:
302
+ break
276
303
 
277
304
  return matched_cmdlines
278
305
 
279
306
 
280
- def run_powershell_command(command):
307
+ def kill_process_by_filename_pattern(pattern: str):
308
+ running_processes = get_running_processes_by_cmdline_pattern(
309
+ pattern, first=False, prefix_suffix=True, cmdline_case_insensitive=True)
310
+
311
+ processes_found: int = len(running_processes)
312
+
313
+ if processes_found > 1:
314
+ raise MultipleProcessesFound(f"[{processes_found}] processes found with pattern '{pattern}'.")
315
+ elif processes_found == 1:
316
+ processes.kill_process_by_pid(running_processes[0]['pid'])
317
+
318
+
319
+ def run_powershell_command(
320
+ command: str
321
+ ):
281
322
  try:
282
323
  result = subprocess.run(["powershell", "-Command", command], capture_output=True, text=True, check=True)
283
- print_api(result.stdout)
324
+ if result.stdout:
325
+ print_api(result.stdout)
326
+ if result.stderr: # PS can write warnings to stderr even on success
327
+ print_api(result.stderr, color='yellow')
284
328
  return result.stdout
285
329
  except subprocess.CalledProcessError as e:
286
- print_api(f"An error occurred: {e}", color='red', error_type=True)
287
- return e
330
+ # e.stderr and e.stdout are populated because capture_output=True
331
+ msg = (e.stderr or e.stdout or f"PowerShell exited with code {e.returncode}")
332
+ print_api(msg, color='red', error_type=True)
333
+ return msg
288
334
 
289
335
 
290
336
  """
File without changes
File without changes
@@ -0,0 +1,95 @@
1
+ from typing import Union, Literal
2
+ import threading
3
+ import time
4
+
5
+ from ... import get_process_list
6
+
7
+
8
+ class PollerPsutilPywin32Dll:
9
+ """
10
+ The class is responsible for getting the list of opened processes by using mentioned libraries.
11
+ """
12
+ def __init__(
13
+ self,
14
+ interval_seconds: Union[int, float] = 0,
15
+ process_get_method: Literal[
16
+ 'poll_psutil',
17
+ 'poll_pywin32',
18
+ 'poll_process_dll'
19
+ ] = 'poll_process_dll',
20
+ process_queue=None
21
+ ):
22
+ """
23
+ :param interval_seconds: works only for pollers, float, how many seconds to wait between each cycle.
24
+ Default is 0, which means that the polling will be as fast as possible.
25
+
26
+ Basically, you want it to be '0' if you want to get the most recent processes.
27
+ Any polling by itself takes time, so if you want to get the most recent processes, you want to do it as fast
28
+ as possible.
29
+ :param process_get_method: str. Default is 'process_dll'. Available:
30
+ 'poll_psutil': Poller, Get the list of processes by 'psutil' library. Resource intensive and slow.
31
+ 'poll_pywin32': Poller, processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
32
+ 'poll_process_dll'. Poller, Not resource intensive and fast. Probably works only in Windows 10 x64.
33
+ 'trace_sysmon_etw': Tracer, Get the list of processes with running SysMon by ETW - Event Tracing.
34
+ In this case 'interval_seconds' is irrelevant, since the ETW is real-time.
35
+ Steps we take:
36
+ 1. Check if SysMon is Running. If not, check if the executable exists in specified
37
+ location and start it as a service.
38
+ 2. Start the "Microsoft-Windows-Sysmon" ETW session.
39
+ 3. Take a snapshot of current processes and their CMDs with psutil and store it in a dict.
40
+ 4. Each new process creation from ETW updates the dict.
41
+ 'trace_event_log': Get the list of processes by subscribing to the Windows Event Log.
42
+ Log Channel: Security, Event ID: 4688.
43
+ We enable the necessary prerequisites in registry and subscribe to the event.
44
+ :param process_queue: Queue. The queue to put the processes in. If None, the processes will not be put in the
45
+ queue.
46
+ """
47
+
48
+ self.interval_seconds: Union[int, float] = interval_seconds
49
+ self.process_get_method = process_get_method.replace('poll_', '')
50
+ self.process_queue = process_queue
51
+
52
+ # noinspection PyTypeChecker
53
+ self.poller_instance = get_process_list.GetProcessList(get_method=self.process_get_method)
54
+
55
+ self._processes = {}
56
+
57
+ def start(self):
58
+ """
59
+ Start the poller.
60
+ """
61
+
62
+ thread = threading.Thread(target=self.emit_loop)
63
+ thread.daemon = True
64
+ thread.start()
65
+
66
+ def emit_loop(self):
67
+ """
68
+ Get the list of processes.
69
+ """
70
+
71
+ self.poller_instance.connect()
72
+
73
+ while True:
74
+ current_processes = self.poller_instance.get_processes(as_dict=True)
75
+
76
+ # Remove Command lines that contains only numbers, since they are useless.
77
+ for pid, process_info in current_processes.items():
78
+ if process_info['cmdline'].isnumeric():
79
+ current_processes[pid]['cmdline'] = str()
80
+ elif process_info['cmdline'] == 'Error':
81
+ current_processes[pid]['cmdline'] = str()
82
+
83
+ # This loop is essential for keeping the command lines.
84
+ # When the process unloads from memory, the last polling will have only pid and executable name, but not
85
+ # the command line. This loop will keep the command line from the previous polling if this happens.
86
+ for pid, process_info in current_processes.items():
87
+ if pid in self._processes:
88
+ if self._processes[pid]['name'] == current_processes[pid]['name']:
89
+ if current_processes[pid]['cmdline'] == '':
90
+ current_processes[pid]['cmdline'] = self._processes[pid]['cmdline']
91
+ self._processes.update(current_processes)
92
+
93
+ self.process_queue.put(self._processes)
94
+
95
+ time.sleep(self.interval_seconds)
@@ -0,0 +1,207 @@
1
+ import threading
2
+ import multiprocessing
3
+ import time
4
+ from typing import Literal, Union
5
+
6
+ from .tracers import sysmon_etw, event_log
7
+ from .pollers import psutil_pywin32wmi_dll
8
+ from ..wrappers.pywin32w.win_event_log.subscribes import process_terminate
9
+
10
+
11
+ POLLER_SETTINGS_SYSMON_ETW: dict = {
12
+ 'etw_session_name': None, # Default will be used, check 'trace_sysmon_process_creation' for details.
13
+ 'sysmon_directory': None # Directory, where sysmon.exe is located. Default will be used.
14
+ }
15
+
16
+
17
+ class ProcessPool:
18
+ """
19
+ The class is responsible for getting processes and storing them in a pool.
20
+ THE POOL OF PROCESSES IS NOT REAL TIME!!!
21
+ There can be several moments delay (less than a second) + you can add delay before pid removal from the pool.
22
+ This is needed only to correlate PIDs to process names and command lines of other events you get on Windows.
23
+ """
24
+ def __init__(
25
+ self,
26
+ operation: Literal['thread', 'process'] = 'thread',
27
+ interval_seconds: Union[int, float] = 0,
28
+ process_get_method: Literal[
29
+ 'poll_psutil',
30
+ 'poll_pywin32',
31
+ 'poll_process_dll',
32
+ 'trace_sysmon_etw',
33
+ 'trace_event_log'
34
+ ] = 'trace_event_log',
35
+ poller_settings: dict = None,
36
+ process_terminator: bool = True,
37
+ wait_before_pid_remove_seconds: float = 5
38
+ ):
39
+ """
40
+ :param operation: str, 'thread' or 'process'. Default is 'process'.
41
+ 'process': The polling will be done in a new process.
42
+ 'thread': The polling will be done in a new thread.
43
+
44
+ Python is slow, if you are going to use 'thread' all other operations inside this thread will be very slow.
45
+ You can even get exceptions, if you're using process polling for correlations of PIDs and process names.
46
+ It is advised to use the 'process' operation, which will not affect other operations in the thread.
47
+ :param interval_seconds: works only for pollers, float, how many seconds to wait between each cycle.
48
+ Default is 0, which means that the polling will be as fast as possible.
49
+
50
+ Basically, you want it to be '0' if you want to get the most recent processes.
51
+ Any polling by itself takes time, so if you want to get the most recent processes, you want to do it as fast
52
+ as possible.
53
+ :param process_get_method: str. Default is 'process_dll'. Available:
54
+ 'poll_psutil': Poller, Get the list of processes by 'psutil' library. Resource intensive and slow.
55
+ 'poll_pywin32': Poller, processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
56
+ 'poll_process_dll'. Poller, Not resource intensive and fast. Probably works only in Windows 10 x64.
57
+ 'trace_sysmon_etw': Tracer, Get the list of processes with running SysMon by ETW - Event Tracing.
58
+ In this case 'interval_seconds' is irrelevant, since the ETW is real-time.
59
+ Steps we take:
60
+ 1. Check if SysMon is Running. If not, check if the executable exists in specified
61
+ location and start it as a service.
62
+ 2. Start the "Microsoft-Windows-Sysmon" ETW session.
63
+ 3. Take a snapshot of current processes and their CMDs with psutil and store it in a dict.
64
+ 4. Each new process creation from ETW updates the dict.
65
+ 'trace_event_log': Get the list of processes by subscribing to the Windows Event Log.
66
+ Log Channel: Security, Event ID: 4688.
67
+ We enable the necessary prerequisites in registry and subscribe to the event.
68
+ :param poller_settings: dict, settings for the poller method.
69
+ 'sysmon_etw': If not set 'POLLER_SETTINGS_SYSMON_ETW' will be used.
70
+ :param process_terminator: bool, if True, process terminator will run in the background and monitor
71
+ the processes for termination. If the process is terminated it will be removed from the pool.
72
+ :param wait_before_pid_remove_seconds: float, how many seconds to wait before the process is removed from
73
+ the pool after process termination event is received for that pid.
74
+ ---------------------------------------------
75
+ If there is an exception, ProcessPollerPool.processes will be set to the exception.
76
+ While getting the processes you can use this to execute the exception:
77
+
78
+ processes = ProcessPollerPool.processes
79
+
80
+ if isinstance(processes, BaseException):
81
+ raise processes
82
+ """
83
+
84
+ self.operation: str = operation
85
+ self.interval_seconds: float = interval_seconds
86
+ self.process_get_method = process_get_method
87
+ self.process_terminator: bool = process_terminator
88
+ self.wait_before_pid_remove_seconds: float = wait_before_pid_remove_seconds
89
+ self.poller_settings: dict = poller_settings
90
+
91
+ if self.poller_settings is None:
92
+ if process_get_method == 'sysmon_etw':
93
+ self.poller_settings: dict = POLLER_SETTINGS_SYSMON_ETW
94
+
95
+ # Current process pool.
96
+ self._processes: dict = dict()
97
+
98
+ # The variable is responsible to stop the thread if it is running.
99
+ self._running: bool = False
100
+
101
+ self._process_queue = multiprocessing.Queue()
102
+ self._running_state_queue = multiprocessing.Queue()
103
+
104
+ def start(self):
105
+ if self.operation == 'thread':
106
+ self._start_thread()
107
+ elif self.operation == 'process':
108
+ self._start_process()
109
+ else:
110
+ raise ValueError(f'Invalid operation type [{self.operation}]')
111
+
112
+ thread_get_queue = threading.Thread(target=self._thread_get_queue)
113
+ thread_get_queue.daemon = True
114
+ thread_get_queue.start()
115
+
116
+ if self.process_terminator:
117
+ thread_process_termination = threading.Thread(target=self._thread_process_termination)
118
+ thread_process_termination.daemon = True
119
+ thread_process_termination.start()
120
+
121
+ def stop(self):
122
+ self._running = False
123
+ self._running_state_queue.put(False)
124
+
125
+ def get_processes(self):
126
+ return self._processes
127
+
128
+ def _get_args_for_worker(self):
129
+ return self.process_get_method, self.interval_seconds, self._process_queue, self.poller_settings
130
+
131
+ def _start_thread(self):
132
+ self._running = True
133
+
134
+ thread = threading.Thread(target=_worker, args=(self._get_args_for_worker()))
135
+ thread.daemon = True
136
+ thread.start()
137
+
138
+ def _start_process(self):
139
+ self._running = True
140
+ multiprocessing.Process(target=_worker, args=(self._get_args_for_worker())).start()
141
+
142
+ def _thread_get_queue(self):
143
+ while True:
144
+ self._processes = self._process_queue.get()
145
+
146
+ def _thread_process_termination(self):
147
+ process_terminate_instance = process_terminate.ProcessTerminateSubscriber()
148
+ process_terminate_instance.start()
149
+
150
+ while True:
151
+ termination_event = process_terminate_instance.emit()
152
+ process_id = termination_event['ProcessIdInt']
153
+
154
+ removal_thread = threading.Thread(target=self._remove_pid, args=(process_id,))
155
+ removal_thread.daemon = True
156
+ removal_thread.start()
157
+
158
+ def _remove_pid(self, process_id):
159
+ # We need to wait a bit before we remove the process.
160
+ # This is because termination event can come sooner than the creation and the process
161
+ # is not yet in the pool.
162
+ # This happens mostly when the process is terminated immediately after the creation.
163
+ # Example: ping example.c
164
+ # 'example.c' is not a valid address, so the process is terminated immediately after the creation.
165
+ counter = 0
166
+ while counter < 30:
167
+ if process_id in self._processes:
168
+ break
169
+ counter += 1
170
+ time.sleep(0.1)
171
+
172
+ if counter == 30:
173
+ # print_api(f'Process [{process_id}] not found in the pool.', color='yellow')
174
+ return
175
+
176
+ # time.sleep(1)
177
+ # if process_id not in self._processes:
178
+ # print_api(f'Process [{process_id}] not found in the pool.', color='red')
179
+ # return
180
+
181
+ time.sleep(self.wait_before_pid_remove_seconds)
182
+ _ = self._processes.pop(process_id, None)
183
+ # print_api(f'Process [{process_id}] removed from the pool.', color='yellow')
184
+
185
+
186
+ def _worker(
187
+ poller_method,
188
+ interval_seconds,
189
+ process_queue,
190
+ poller_settings
191
+ ):
192
+
193
+ if poller_method == 'trace_sysmon_etw':
194
+ get_instance = sysmon_etw.TracerSysmonEtw(settings=poller_settings, process_queue=process_queue)
195
+ elif poller_method == 'trace_event_log':
196
+ get_instance = event_log.TracerEventlog(process_queue=process_queue)
197
+ elif 'poll_' in poller_method:
198
+ get_instance = psutil_pywin32wmi_dll.PollerPsutilPywin32Dll(
199
+ interval_seconds=interval_seconds, process_get_method=poller_method, process_queue=process_queue)
200
+ else:
201
+ raise ValueError(f'Invalid poller method [{poller_method}]')
202
+
203
+ # We must initiate the connection inside the thread/process, because it is not thread-safe.
204
+ get_instance.start()
205
+
206
+ while True:
207
+ time.sleep(1)