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/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.
@@ -22,8 +22,12 @@ def threaded_periodic_task(interval, function_ref, args=(), kwargs=None, thread_
22
22
  :param args: tuple, of arguments to provide for the 'function_ref' to execute.
23
23
  :param kwargs: dictionary, of keyword arguments to provide for the 'function_ref' to execute.
24
24
  :param thread_name: the name of the thread that will be created:
25
- threading.Thread(target=thread_timer, name=thread_name).start()
25
+ threading.Thread(target=thread_timer, name=thread_name)
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:
@@ -60,7 +70,14 @@ class ThreadLooper:
60
70
  def __init__(self):
61
71
  self.loop_queue = queue.Queue()
62
72
 
63
- def run_loop(self, function_reference, args=(), kwargs=None, interval_seconds=0, thread_name: str = None):
73
+ def run_loop(
74
+ self,
75
+ function_reference,
76
+ args=(),
77
+ kwargs=None,
78
+ interval_seconds=0,
79
+ thread_name: str = None
80
+ ):
64
81
  """
65
82
  The function executes referenced function 'function_ref' with arguments 'args' each 'interval' in a new thread.
66
83
 
@@ -86,7 +103,9 @@ class ThreadLooper:
86
103
  self.loop_queue.put(result_list)
87
104
  time.sleep(interval_seconds)
88
105
 
89
- threading.Thread(target=thread_function, name=thread_name).start()
106
+ thread = threading.Thread(target=thread_function, name=thread_name)
107
+ thread.daemon = True
108
+ thread.start()
90
109
 
91
110
  def emit_from_loop(self):
92
111
  """
atomicshop/sound.py CHANGED
@@ -280,11 +280,13 @@ class StereoMixRecorder:
280
280
  # when playback can start before the input interface is initialized, since thread runs in parallel.
281
281
  self.loopback_input = self._initialize_input_interface()
282
282
 
283
- threading.Thread(
283
+ thread = threading.Thread(
284
284
  target=self._thread_record,
285
285
  args=(split_emit_buffers, emit_type, file_path, record_until_zero_array, seconds),
286
286
  kwargs=kwargs
287
- ).start()
287
+ )
288
+ thread.daemon = True
289
+ thread.start()
288
290
 
289
291
  def _thread_record(
290
292
  self, split_emit_buffers: int, emit_type: str, file_path: str, record_until_zero_array: bool, seconds,
@@ -1,3 +1,11 @@
1
+ # TODO: Change manual wrapper to:
2
+ # from ffmpy import FFmpeg
3
+ # ff = FFmpeg(
4
+ # inputs={'input.mp4': None},
5
+ # outputs={'output.avi': None}
6
+ # )
7
+ # ff.run()
8
+
1
9
  from .wrappers.ffmpegw import FFmpegWrapper
2
10
  from .tempfiles import TempFile
3
11
  from .web import download
atomicshop/ssh_remote.py CHANGED
@@ -1,19 +1,23 @@
1
1
  import sys
2
- import base64
3
- import socket
2
+ import logging
3
+ from pathlib import Path
4
+ import shlex
4
5
 
5
- from .print_api import print_api
6
- from .wrappers.loggingw import loggingw
7
- from .wrappers.socketw import base
8
-
9
-
10
- # External Libraries
11
6
  try:
12
7
  import paramiko
13
8
  except ImportError as exception_object:
14
9
  print(f"Library missing: {exception_object.name}. Install by executing: pip install paramiko")
15
10
  sys.exit()
16
11
 
12
+ from .print_api import print_api
13
+ from .wrappers.loggingw import loggingw
14
+ from .wrappers.socketw import socket_base
15
+
16
+
17
+ class SSHRemoteWrapperNoPythonFound(Exception):
18
+ """Raised when no usable Python 3 interpreter found on remote host."""
19
+ pass
20
+
17
21
 
18
22
  class SSHRemote:
19
23
  """
@@ -87,9 +91,13 @@ class SSHRemote:
87
91
  sys.exit(main())
88
92
 
89
93
  """
90
- logger = loggingw.get_logger_with_level("network." + __name__.rpartition('.')[2])
91
-
92
- def __init__(self, ip_address: str, username: str, password: str):
94
+ def __init__(
95
+ self,
96
+ ip_address: str,
97
+ username: str,
98
+ password: str,
99
+ logger: logging.Logger = None
100
+ ):
93
101
  self.ip_address: str = ip_address
94
102
  self.username: str = username
95
103
  self.password: str = password
@@ -97,11 +105,23 @@ class SSHRemote:
97
105
  # Initializing paramiko SSHClient class
98
106
  self.ssh_client = paramiko.SSHClient()
99
107
 
100
- def connect(self):
108
+ # Variable to store detected python command on remote (python3 / python).
109
+ self.python_cmd: str | None = None
110
+
111
+ if logger:
112
+ # Create child logger for the provided logger with the module's name.
113
+ self.logger: logging.Logger = loggingw.get_logger_with_level(f'{logger.name}.{Path(__file__).stem}')
114
+ else:
115
+ self.logger: logging.Logger = logger
116
+
117
+ def connect(
118
+ self,
119
+ timeout: int = 60
120
+ ):
101
121
  error: str = str()
102
122
 
103
123
  # Get all local interfaces IPv4 addresses.
104
- local_interfaces_ipv4 = base.get_local_network_interfaces_ip_address("ipv4", True)
124
+ local_interfaces_ipv4 = socket_base.get_local_network_interfaces_ip_address("ipv4", True)
105
125
  # Check if the target IP address is in the list of local interfaces.
106
126
  if self.ip_address in local_interfaces_ipv4:
107
127
  # If it is, we don't need to connect to it via SSH, it means that we want to connect to ourselves.
@@ -119,75 +139,15 @@ class SSHRemote:
119
139
  # with description of
120
140
  # Server 'address_goes_here' not found in known_hosts
121
141
  self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
122
- try:
123
- # Executing SSH connection to client.
124
- self.ssh_client.connect(self.ip_address, username=self.username, password=self.password, timeout=60)
125
- # When port 22 is unreachable on the client.
126
- except paramiko.ssh_exception.NoValidConnectionsError as e:
127
- error = str(e)
128
- # Logging the error also. Since the process name isn't critical, we'll continue script execution.
129
- print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
130
- pass
131
- except paramiko.ssh_exception.SSHException as e:
132
- error = str(e)
133
- # Logging the error also. Since the process name isn't critical, we'll continue script execution.
134
- print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
135
- pass
136
- except ConnectionResetError:
137
- # Returning the error.
138
- error = "An existing connection was forcibly closed by the remote host."
139
- # Logging the error also. Since the process name isn't critical, we'll continue script execution.
140
- print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
141
- pass
142
- except TimeoutError:
143
- # Returning the error.
144
- error = "Connection timed out."
145
- # Logging the error also. Since the process name isn't critical, we'll continue script execution.
146
- print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
147
- pass
142
+
143
+ # Executing SSH connection to client.
144
+ self.ssh_client.connect(self.ip_address, username=self.username, password=self.password, timeout=timeout)
148
145
 
149
146
  return error
150
147
 
151
148
  def close(self):
152
149
  self.ssh_client.close()
153
150
 
154
- def exec_command_with_error_handling(self, script_string: str):
155
- # Defining variables.
156
- stdin = None
157
- stdout = None
158
- stderr = None
159
- result_exception = None
160
-
161
- # Don't put debugging break point over the next line in PyCharm. For some reason it gets stuck.
162
- # Put the point right after that.
163
- try:
164
- stdin, stdout, stderr = self.ssh_client.exec_command(command=script_string, timeout=30)
165
- except AttributeError as function_exception_object:
166
- if function_exception_object.name == "open_session":
167
- result_exception = "'SSHRemote().connect' wasn't executed."
168
- print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
169
-
170
- # Since getting Process name is not the main feature of the server, we can pass the exception
171
- pass
172
- else:
173
- result_exception = f"Couldn't execute script over SSH. Unknown yet exception with 'AttributeError' " \
174
- f"and name: {function_exception_object.name}"
175
- print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
176
- # Since getting Process name is not the main feature of the server, we can pass the exception
177
- pass
178
- except socket.error:
179
- result_exception = "Couldn't execute script over SSH. SSH socket closed."
180
- print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
181
- # Since getting Process name is not the main feature of the server, we can pass the exception
182
- pass
183
- except Exception:
184
- result_exception = "Couldn't execute script over SSH. Unknown yet exception."
185
- print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
186
- # Since getting Process name is not the main feature of the server, we can pass the exception
187
- pass
188
-
189
- return stdin, stdout, stderr, result_exception
190
-
191
151
  @staticmethod
192
152
  def check_console_output_for_errors(console_output_string: str):
193
153
  # Defining variables.
@@ -202,104 +162,132 @@ class SSHRemote:
202
162
  if "ModuleNotFoundError: No module named" in line:
203
163
  function_result = f"Python library is not installed - {line}"
204
164
  break
165
+ else:
166
+ function_result = console_output_string
205
167
 
206
168
  return function_result
207
169
 
208
- def remote_execution(self, script_string: str):
209
- # Defining variables.
210
- output_lines = None
211
- function_error = None
212
- stdin = None
213
- stdout = None
214
- stderr = None
215
- exec_exception = None
170
+ def remote_execution(
171
+ self,
172
+ command: str,
173
+ script_string: str = None
174
+ ) -> tuple[str, str]:
175
+ """
176
+ Function to execute any command over SSH.
177
+
178
+ :param command: command to execute over SSH.
179
+ :param script_string: string representation of script to execute as input.
180
+ Example:
181
+ command = "python - 56734"
182
+ script_string = "import sys;print(f'sys.argv[0]')"
183
+
184
+ Under ssh in terminal it would execute like this:
185
+ ssh User@HostIpv4
186
+
187
+ python - 56734 << EOF
188
+ import sys;print(f'sys.argv[0]')
189
+ EOF
190
+
191
+ Or as onliner:
192
+ ssh User@HostIpv4 "python - 56734 << EOF import sys;print(f'sys.argv[0]') EOF"
193
+
194
+ or using a specific file path:
195
+ ssh User@HostIpv4 "python - 56734" < /path/to/script.py
196
+
197
+ :return: SSH console output, Error output
198
+ """
199
+ output_result: str = str()
200
+ error_result: str = str()
216
201
 
217
202
  # Execute the command over SSH remotely.
218
- stdin, stdout, stderr, exec_exception = self.exec_command_with_error_handling(script_string)
219
- # If exception was returned from execution.
220
- if exec_exception:
221
- self.logger.info("Trying to reconnect over SSH.")
222
- # Close existing SSH Connection.
223
- self.close()
224
- # And connect again.
225
- self.connect()
226
- self.logger.info("Reconnected. Trying to send the command one more time.")
227
- # Try to execute the command over SSH remotely again.
228
- stdin, stdout, stderr, exec_exception = self.exec_command_with_error_handling(script_string)
229
- # If there was an exception again.
230
- if exec_exception:
231
- # Populate the function_error variable that will be returned outside.
232
- function_error = exec_exception
233
-
234
- # If there was no exception executing the remote command.
235
- if not function_error:
236
- # Reading the buffer of stdout.
237
- output_lines = stdout.readlines()
238
- # Reading the buffer of stderr.
239
- function_error = stderr.readlines()
240
-
241
- # Joining error lines list to string if not empty.
242
- if function_error:
243
- function_error = ''.join(function_error)
244
- # Else, joining output lines to string.
245
- else:
246
- output_lines = ''.join(output_lines)
203
+ stdin, stdout, stderr = self.ssh_client.exec_command(command=command, timeout=30)
204
+
205
+ # Writing the script string into stdin buffer.
206
+ if script_string:
207
+ stdin.write(script_string)
208
+ stdin.channel.shutdown_write()
247
209
 
248
- # Since they're "file-like" objects we need to close them after we finished using.
249
- stdin.close()
250
- stdout.close()
251
- stderr.close()
210
+ # Reading the buffer of stdout.
211
+ output_lines: list = stdout.readlines()
212
+ # Reading the buffer of stderr.
213
+ error_lines: list = stderr.readlines()
252
214
 
253
- return output_lines, function_error
215
+ # Joining error lines list to string if not empty.
216
+ if error_lines:
217
+ error_result: str = ''.join(error_lines)
218
+ # Else, joining output lines to string.
219
+ else:
220
+ output_result = ''.join(output_lines)
254
221
 
255
- def remote_execution_python(self, script_string: str):
222
+ # Since they're "file-like" objects we need to close them after we finished using.
223
+ stdin.close()
224
+ stdout.close()
225
+ stderr.close()
226
+
227
+ return output_result, error_result
228
+
229
+ def _detect_remote_python_cmd_name(self) -> str:
230
+ """
231
+ Try 'python3' then 'python' on the remote, return the one that is Python 3.
232
+ Raises if neither works.
233
+ """
234
+ for candidate in ("python3", "python"):
235
+ # Use a simple version check that works on both Windows and Linux
236
+ cmd = f'{candidate} -c "import sys; print(sys.version_info[0])"'
237
+ stdin, stdout, stderr = self.ssh_client.exec_command(cmd, timeout=5)
238
+
239
+ out = stdout.read().decode().strip()
240
+ exit_status = stdout.channel.recv_exit_status()
241
+
242
+ if exit_status == 0 and out == "3":
243
+ print_api(f"Detected remote Python 3 interpreter (once per client port): {candidate}", logger=self.logger)
244
+ return candidate
245
+
246
+ raise SSHRemoteWrapperNoPythonFound("No usable Python 3 interpreter found on remote host")
247
+
248
+ def _get_python_cmd(self) -> str:
249
+ if self.python_cmd is None:
250
+ self.python_cmd = self._detect_remote_python_cmd_name()
251
+ return self.python_cmd
252
+
253
+ def remote_execution_python(
254
+ self,
255
+ script_string: str,
256
+ script_arg_values: tuple = None,
257
+ script_kwargs: dict = None,
258
+ ):
256
259
  """
257
260
  Function to execute python script over SSH.
258
261
 
259
- Example:
260
- network.logger.info("Initializing SSH connection to get the calling process.")
261
-
262
- # Initializing SSHRemote class.
263
- ssh_client = SSHRemote(ip_address=client_message.client_ip, username=username, password=password)
264
- # Making actual SSH Connection to the computer.
265
- ssh_connection_error = ssh_client.connect()
266
- # If there's an exception / error during connection.
267
- if ssh_connection_error:
268
- # Put the error in the process name value.
269
- client_message.process_name = ssh_connection_error
270
- else:
271
- # If no error, then initialize the variables for python script execution over SSH.
272
- remote_output = remote_error = None
273
-
274
- # Put source port variable inside the string script.
275
- script_string: str = \
276
- put_variable_into_string_script(ssh_script_port_by_process_string, client_message.source_port)
277
-
278
- # Execute the python script on remote computer over SSH.
279
- remote_output, remote_error = ssh_client.remote_execution_python(script_string)
280
-
281
- # If there was an error during execution, put it in process name.
282
- if remote_error:
283
- client_message.process_name = remote_error
284
- # If there was no error during execution, put the output of the ssh to process name.
285
- else:
286
- client_message.process_name = remote_output
287
- network.logger.info(f"Remote SSH: Client executing Command Line: {client_message.process_name}")
288
-
289
262
  :param script_string: string representation of python script.
263
+ :param script_arg_values: values arguments to pass to the script. Example for first argument: 56734
264
+ :param script_kwargs: keyword arguments to pass to the script.
265
+ Example: {'-r': None}
266
+ Interpreted as: -r
267
+ Example: {'-f': 'value'}
268
+ Interpreted as: -f value
269
+ Example: {'--arg': value}
270
+ Interpreted as: --arg value
271
+
290
272
  :return: SSH console output, Error output
291
273
  """
292
274
  # Defining variables.
293
- function_return = None
294
- function_error = None
275
+ error_result: str | None = None
276
+
277
+ python_cmd = self._get_python_cmd()
278
+ command: str = f"{python_cmd} -"
295
279
 
296
- encoded_base64_string = base64.b64encode(script_string.encode('ascii'))
297
- command_string: str = fr'python -c "import base64;exec(base64.b64decode({encoded_base64_string}))"'
280
+ if script_arg_values:
281
+ for arg in script_arg_values:
282
+ command += " " + shlex.quote(str(arg))
298
283
 
299
- # remote_output, remote_error = ssh_client.remote_execution('ipconfig')
300
- # remote_output, remote_error = ssh_client.remote_execution("python -c print('Hello')")
301
- # remote_output, remote_error = ssh_client.remote_execution("python -c import psutil")
302
- remote_output, remote_error = self.remote_execution(command_string)
284
+ if script_kwargs:
285
+ for key, value in script_kwargs.items():
286
+ command += f" {shlex.quote(str(key))}"
287
+ if value is not None:
288
+ command += " " + shlex.quote(str(value))
289
+
290
+ remote_output, remote_error = self.remote_execution(command=command, script_string=script_string)
303
291
 
304
292
  # If there was an error during remote execution
305
293
  if remote_error:
@@ -308,37 +296,35 @@ class SSHRemote:
308
296
  # If the message is known and didn't return empty.
309
297
  if console_check:
310
298
  # 'execution_error' variable will be that full error.
311
- function_error = console_check
299
+ error_result = console_check
300
+ else:
301
+ error_result = remote_error
312
302
 
313
- return remote_output, function_error
303
+ return remote_output, error_result
314
304
 
315
- def connect_get_client_commandline(self, script_string):
305
+ def connect_get_client_commandline(
306
+ self,
307
+ port: int,
308
+ script_string: str):
316
309
  # Defining locals.
317
- execution_output = None
318
- execution_error = None
310
+ execution_output: str | None = None
319
311
 
320
312
  # Making actual SSH Connection to the computer.
321
313
  execution_error = self.connect()
322
314
  # if there was an error, try to connect again.
323
315
  if execution_error:
324
- self.logger.info("Retrying SSH Connection Initialization.")
316
+ print_api("Retrying SSH Connection Initialization.", logger=self.logger, logger_method='info')
325
317
  execution_error = self.connect()
326
318
 
327
319
  # If there was still an error, we won't be executing the script. And the error will be passed to
328
320
  # 'process_name'.
329
321
  if not execution_error:
330
- self.logger.info("Executing SSH command to acquire the calling process.")
322
+ print_api("Executing SSH command to acquire the calling process.", logger=self.logger, logger_method='info')
331
323
 
332
- try:
333
- execution_output, execution_error = self.remote_execution_python(script_string)
334
- # Basically we don't care much about SSH exceptions. Just log them and pass to record.
335
- except Exception as function_exception_object:
336
- execution_error = function_exception_object
337
- print_api(execution_error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
338
- pass
324
+ execution_output, execution_error = self.remote_execution_python(script_string=script_string, script_arg_values=(str(port),))
339
325
 
340
326
  # Closing SSH connection at this stage.
341
327
  self.close()
342
- self.logger.info("Acquired. Closed SSH connection.")
328
+ print_api("Acquired. Closed SSH connection.", logger=self.logger, logger_method='info')
343
329
 
344
330
  return execution_output, execution_error
File without changes
File without changes
@@ -0,0 +1,53 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ from ...wrappers.pywin32w import winshell
5
+
6
+
7
+ STARTUP_FOLDER = os.path.join(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
8
+
9
+
10
+ def add_to_startup_folder_with_shortcut(exe_file_path: str, shortcut_name: str) -> str:
11
+ """
12
+ This function will create a shortcut in the startup folder to your executable.
13
+
14
+ :param exe_file_path: The path to your executable file.
15
+ :param shortcut_name: The name of the shortcut file to create in the startup folder.
16
+ No need to add the ".lnk" extension.
17
+ :return: The path to the shortcut file created.
18
+ """
19
+
20
+ # Get the startup folder path and create if non-existent.
21
+ Path(STARTUP_FOLDER).mkdir(parents=True, exist_ok=True)
22
+
23
+ shortcut_file_path = str(Path(STARTUP_FOLDER, f'{shortcut_name}.lnk'))
24
+
25
+ # Create a shortcut to the executable file.
26
+ winshell.create_shortcut(exe_file_path, shortcut_file_path)
27
+
28
+ return shortcut_file_path
29
+
30
+
31
+ def is_in_startup_folder(shortcut_name: str):
32
+ """
33
+ This function will check if the shortcut is in the startup folder.
34
+
35
+ :param shortcut_name: The name of the shortcut file to check in the startup folder.
36
+ No need to add the ".LNK" extension.
37
+ :return: True if the shortcut is in the startup folder, False otherwise.
38
+ """
39
+ return Path(STARTUP_FOLDER, f'{shortcut_name}.lnk').exists()
40
+
41
+
42
+ def remove_from_startup_folder(shortcut_name: str):
43
+ """
44
+ This function will remove the shortcut from the startup folder.
45
+
46
+ :param shortcut_name: The name of the shortcut file to remove from the startup folder.
47
+ No need to add the ".LNK" extension.
48
+ """
49
+ shortcut_file_path = Path(STARTUP_FOLDER, f'{shortcut_name}.lnk')
50
+ if shortcut_file_path.exists():
51
+ shortcut_file_path.unlink()
52
+ return True
53
+ return False