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,133 @@
1
+ from typing import Union, Literal
2
+
3
+ from .wrappers.pywin32w.wmis import win32process
4
+ from .wrappers.psutilw import psutilw
5
+ from .basics import dicts
6
+ from . import get_process_name_cmd_dll
7
+
8
+
9
+ class GetProcessList:
10
+ """
11
+ The class is responsible for getting the list of running processes.
12
+
13
+ Example of one time polling with 'pywin32' method:
14
+ from atomicshop import process_poller
15
+ process_list: dict = \
16
+ process_poller.GetProcessList(get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
17
+ """
18
+ def __init__(
19
+ self,
20
+ get_method: Literal['psutil', 'pywin32', 'process_dll'] = 'process_dll',
21
+ connect_on_init: bool = False
22
+ ):
23
+ """
24
+ :param get_method: str, The method to get the list of processes. Default is 'process_list_dll'.
25
+ 'psutil': Get the list of processes by 'psutil' library. Resource intensive and slow.
26
+ 'pywin32': Get the list of processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
27
+ 'process_dll'. Not resource intensive and fast. Probably works only in Windows 10 x64.
28
+ :param connect_on_init: bool, if True, will connect to the service on init. 'psutil' don't need to connect.
29
+ """
30
+ self.get_method = get_method
31
+ self.process_polling_instance = None
32
+
33
+ self.connected = False
34
+
35
+ if self.get_method == 'psutil':
36
+ self.process_polling_instance = psutilw.PsutilProcesses()
37
+ self.connected = True
38
+ elif self.get_method == 'pywin32':
39
+ self.process_polling_instance = win32process.Pywin32Processes()
40
+ elif self.get_method == 'process_dll':
41
+ self.process_polling_instance = get_process_name_cmd_dll.ProcessNameCmdline()
42
+
43
+ if connect_on_init:
44
+ self.connect()
45
+
46
+ def connect(self):
47
+ """
48
+ Connect to the service. 'psutil' don't need to connect.
49
+ """
50
+
51
+ # If poller method is none of the allowed methods.
52
+ if self.get_method not in ['psutil', 'pywin32', 'process_dll']:
53
+ raise ValueError(f"Method '{self.get_method}' is not allowed.")
54
+
55
+ # If the service is not connected yet. Since 'psutil' don't need to connect.
56
+ if not self.connected:
57
+ if self.get_method == 'pywin32':
58
+ self.process_polling_instance.connect()
59
+ self.connected = True
60
+ elif self.get_method == 'process_dll':
61
+ self.process_polling_instance.load()
62
+ self.connected = True
63
+
64
+ def get_processes(self, as_dict: bool = True) -> Union[list, dict]:
65
+ """
66
+ The function will get the list of opened processes and return it as a list of dicts.
67
+
68
+ :return: dict while key is pid or list of dicts, of opened processes (depending on 'as_dict' setting).
69
+ """
70
+
71
+ if as_dict:
72
+ if self.get_method == 'psutil':
73
+ return self.process_polling_instance.get_processes_as_dict(
74
+ attrs=['pid', 'name', 'cmdline'], cmdline_to_string=True)
75
+ elif self.get_method == 'pywin32':
76
+ processes = self.process_polling_instance.get_processes_as_dict(
77
+ attrs=['ProcessId', 'Name', 'CommandLine'])
78
+
79
+ # Convert the keys from WMI to the keys that are used in 'psutil'.
80
+ converted_process_dict = dict()
81
+ for pid, process_info in processes.items():
82
+ converted_process_dict[pid] = dicts.convert_key_names(
83
+ process_info, {'Name': 'name', 'CommandLine': 'cmdline'})
84
+
85
+ return converted_process_dict
86
+ elif self.get_method == 'process_dll':
87
+ return self.process_polling_instance.get_process_details(as_dict=True)
88
+ else:
89
+ if self.get_method == 'psutil':
90
+ return self.process_polling_instance.get_processes_as_list_of_dicts(
91
+ attrs=['pid', 'name', 'cmdline'], cmdline_to_string=True)
92
+ elif self.get_method == 'pywin32':
93
+ processes = self.process_polling_instance.get_processes_as_list_of_dicts(
94
+ attrs=['ProcessId', 'Name', 'CommandLine'])
95
+
96
+ # Convert the keys from WMI to the keys that are used in 'psutil'.
97
+ for process_index, process_info in enumerate(processes):
98
+ processes[process_index] = dicts.convert_key_names(
99
+ process_info, {'ProcessId': 'pid', 'Name': 'name', 'CommandLine': 'cmdline'})
100
+
101
+ return processes
102
+ elif self.get_method == 'process_dll':
103
+ return self.process_polling_instance.get_process_details(as_dict=as_dict)
104
+
105
+
106
+ def get_process_time_tester(
107
+ get_method: Literal['psutil', 'pywin32', 'process_dll'] = 'process_dll',
108
+ times_to_test: int = 50
109
+ ):
110
+ """
111
+ The function will test the time it takes to get the list of processes with different methods and cycles.
112
+
113
+ :param get_method: str, The method to get the list of processes. Default is 'process_list_dll'.
114
+ 'psutil': Get the list of processes by 'psutil' library. Resource intensive and slow.
115
+ 'pywin32': Get the list of processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
116
+ 'process_dll'. Not resource intensive and fast. Probably works only in Windows 10 x64
117
+ :param times_to_test: int, how many times to test the function.
118
+ """
119
+
120
+ import timeit
121
+
122
+ setup_code = '''
123
+ from atomicshop import process_poller
124
+ get_process_list = process_poller.GetProcessList(get_method=get_method, connect_on_init=True)
125
+ '''
126
+
127
+ test_code = '''
128
+ test = get_process_list.get_processes()
129
+ '''
130
+
131
+ # globals need to be specified, otherwise the setup_code won't work with passed variables.
132
+ times = timeit.timeit(setup=setup_code, stmt=test_code, number=times_to_test, globals=locals())
133
+ print(f'Execution time: {times}')
@@ -1,19 +1,25 @@
1
- import pkg_resources
1
+ from importlib.resources import files
2
2
  import ctypes
3
3
  from ctypes import wintypes
4
4
  from typing import Literal
5
+ import threading
5
6
 
6
7
  from .basics import list_of_dicts
7
8
 
8
9
 
10
+ PACKAGE_DLL_PATH = 'addons/process_list/compiled/Win10x64/process_list.dll'
11
+ FULL_DLL_PATH = str(files(__package__).joinpath(PACKAGE_DLL_PATH))
12
+
13
+
9
14
  class ProcessNameCmdline:
10
15
  def __init__(self, load_dll: bool = False):
11
16
  self.load_dll: bool = load_dll
12
- self.dll_path: str = pkg_resources.resource_filename(
13
- __package__, 'addons/process_list/compiled/Win10x64/process_list.dll')
17
+ self.dll_path = FULL_DLL_PATH
18
+
14
19
  self.dll = None
15
20
  self.callback_output = None
16
21
  self.CALLBACKFUNC = None
22
+ self.CALLBACKFUNC_ref = None
17
23
 
18
24
  if self.load_dll:
19
25
  self.load()
@@ -21,12 +27,16 @@ class ProcessNameCmdline:
21
27
  def load(self):
22
28
  """
23
29
  Load the DLL, initialize the callback function and ctypes.
24
- ctypes.WINFUCNTYPE is not thread safe. You should load it inside a thread / process and not outside.
30
+ ctypes.WINFUNCTYPE is not thread safe. You should load it inside a thread / process and not outside.
25
31
  """
26
32
  self.dll = ctypes.windll.LoadLibrary(self.dll_path)
27
33
  self.callback_output = OutputList()
28
34
  self.CALLBACKFUNC = ctypes.WINFUNCTYPE(None, wintypes.DWORD, wintypes.LPWSTR, wintypes.LPWSTR)
35
+ self.CALLBACKFUNC_ref = self.CALLBACKFUNC(self.callback_output.callback)
36
+
37
+ # Set the argument types for the export functions of the DLL.
29
38
  self.dll.GetProcessDetails.argtypes = [self.CALLBACKFUNC]
39
+ self.dll.RequestCancellation.restype = None
30
40
 
31
41
  def get_process_details(
32
42
  self,
@@ -49,23 +59,37 @@ class ProcessNameCmdline:
49
59
  }
50
60
  """
51
61
 
52
- self.dll.GetProcessDetails(self.CALLBACKFUNC(self.callback_output.callback))
62
+ def enumerate_process():
63
+ self.dll.GetProcessDetails(self.CALLBACKFUNC_ref)
64
+
65
+ thread = threading.Thread(target=enumerate_process)
66
+ thread.start()
67
+
68
+ try:
69
+ # This is needed to stop the thread if the main thread is interrupted.
70
+ # If we execute the 'self.dll.GetProcessDetails(self.CALLBACKFUNC_ref)' directly
71
+ # and we would like to KeyboardInterrupt, we will get an error:
72
+ # Exception ignored on calling ctypes callback function.
73
+ thread.join()
53
74
 
54
- processes = self.callback_output.data
75
+ processes = self.callback_output.data
55
76
 
56
- # Clear the callback output list, or it will be appended each time.
57
- self.callback_output.data = list()
77
+ # Clear the callback output list, or it will be appended each time.
78
+ self.callback_output.data = list()
58
79
 
59
- if sort_by:
60
- processes = list_of_dicts.sort_by_keys(processes, key_list=[sort_by])
80
+ if sort_by:
81
+ processes = list_of_dicts.sort_by_keys(processes, key_list=[sort_by])
61
82
 
62
- if as_dict:
63
- processes = convert_processes_to_dict(processes)
83
+ if as_dict:
84
+ processes = convert_processes_to_dict(processes)
64
85
 
65
- return processes
86
+ return processes
87
+ except KeyboardInterrupt:
88
+ self.dll.RequestCancellation()
89
+ raise
66
90
 
67
91
 
68
- def convert_processes_to_dict(process_list: dict) -> dict:
92
+ def convert_processes_to_dict(process_list: list[dict]) -> dict:
69
93
  """
70
94
  The function will convert the list of processes to dict, while 'pid' values will be converted to key.
71
95
  Example:
@@ -99,14 +123,23 @@ class OutputList:
99
123
 
100
124
  def callback(self, pid, process_name, cmdline):
101
125
  try:
126
+ process_name_decoded = process_name.decode("utf-16") if isinstance(process_name, bytes) else process_name
127
+ cmdline_decoded = cmdline.decode("utf-16") if isinstance(cmdline, bytes) else (
128
+ cmdline if cmdline else "Error")
102
129
  self.data.append({
103
130
  "pid": pid,
104
- "name": process_name.decode("utf-16"),
105
- "cmdline": cmdline.decode("utf-16") if cmdline else "Error"
131
+ "name": process_name_decoded,
132
+ "cmdline": cmdline_decoded
106
133
  })
107
- except AttributeError:
134
+ # except AttributeError:
135
+ # self.data.append({
136
+ # "pid": pid,
137
+ # "name": process_name,
138
+ # "cmdline": cmdline if cmdline else "Error"
139
+ # })
140
+ except Exception:
108
141
  self.data.append({
109
142
  "pid": pid,
110
- "name": process_name,
111
- "cmdline": cmdline if cmdline else "Error"
143
+ "name": "Error",
144
+ "cmdline": "Error"
112
145
  })
atomicshop/http_parse.py CHANGED
@@ -1,10 +1,43 @@
1
- # v1.0.1 - 26.03.2023 23:40
2
1
  from http.server import BaseHTTPRequestHandler
3
2
  from http.client import HTTPResponse
4
3
  import http
5
4
  from io import BytesIO
6
5
 
7
6
 
7
+ def get_request_methods() -> list[str]:
8
+ """
9
+ Function to return all available HTTP request methods.
10
+ """
11
+ # noinspection PyUnresolvedReferences
12
+ return [method.value for method in http.HTTPMethod]
13
+
14
+
15
+ def is_first_bytes_http_request(request_bytes: bytes) -> bool:
16
+ """
17
+ Function to check if the first bytes are HTTP request or not.
18
+ """
19
+ http_request_methods_list: list = get_request_methods()
20
+ http_request_methods_list = [method.encode() for method in http_request_methods_list]
21
+
22
+ # If the first bytes are HTTP request, then the first word should be one of the HTTP request methods.
23
+ for method in http_request_methods_list:
24
+ if request_bytes.startswith(method):
25
+ return True
26
+ return False
27
+
28
+
29
+ def is_first_bytes_http_response(response_bytes: bytes) -> bool:
30
+ """
31
+ Function to check if the first bytes are HTTP response or not.
32
+ """
33
+ http_response_beginning: bytes = b'HTTP/'
34
+
35
+ # If the first bytes are HTTP response, then the first word should be 'HTTP/'.
36
+ if response_bytes.startswith(http_response_beginning):
37
+ return True
38
+ return False
39
+
40
+
8
41
  class HTTPRequestParse(BaseHTTPRequestHandler):
9
42
  """
10
43
  The class will parse HTTP requests.
@@ -49,41 +82,27 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
49
82
  client_message.request_raw_decoded = request_decoded
50
83
  """
51
84
 
52
- def __init__(self, request_text):
53
- self.rfile = BytesIO(request_text)
54
- self.raw_requestline = self.rfile.readline()
55
- self.error_code = self.error_message = None
56
- self.parse_request()
57
-
58
- # Check if ".path" attribute exists after HTTP request parsing
59
- if not hasattr(self, 'path'):
60
- self.path = None
85
+ # noinspection PyMissingConstructor
86
+ def __init__(self, request_bytes: bytes):
87
+ self.request_bytes: bytes = request_bytes
61
88
 
62
- # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values
63
- try:
64
- # The "body" of request is in the 'Content-Length' key. If it exists in "headers" - get the body
65
- if 'Content-Length' in self.headers.keys():
66
- # "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
67
- self.content_length = int(self.headers.get('Content-Length'))
68
- self.body = self.rfile.read(self.content_length)
69
- else:
70
- self.content_length = None
71
- self.body = None
72
- except Exception:
73
- self.content_length = None
74
- self.body = None
75
- pass
89
+ # noinspection PyTypeChecker
90
+ self.rfile = None
91
+ self.raw_requestline = None
92
+ self.error_code = None
93
+ self.error_message = None
76
94
 
77
- # Examples:
78
- # Getting path: self.path
79
- # Getting Request Version: self.request_version
80
- # Getting specific header: self.headers['host']
95
+ self.content_length = None
96
+ self.body = None
97
+ # noinspection PyTypeChecker
98
+ self.path = None
81
99
 
100
+ # noinspection PyMethodOverriding
82
101
  def send_error(self, code, message):
83
102
  self.error_code = code
84
103
  self.error_message = message
85
104
 
86
- def check_if_http(self):
105
+ def parse(self):
87
106
  """
88
107
  Function to check if parsed object is HTTP request or not.
89
108
  'reason' will be populated with parsing status and errors.
@@ -117,88 +136,125 @@ class HTTPRequestParse(BaseHTTPRequestHandler):
117
136
 
118
137
  client_message.request_raw_decoded = request_decoded
119
138
  """
120
- # Defining variables
121
- reason = None
122
- function_result: bool = True
123
- error: bool = False
124
-
125
- # If there's any error in HTTP parsing
126
- if self.error_message:
127
- # If error status is "BAD_REQUEST"
128
- if self.error_message.startswith("Bad request"):
129
- # If it's 'Bad request' this is not HTTP request, so we can
130
- # continue the execution and parse the code as NON-HTTP Request.
131
- # Currently, seen 'Bad request syntax' and 'Bad request version'.
132
- reason = f"HTTP Request Parsing: Not HTTP request: {self.error_message}"
133
- function_result = False
134
- error: False
135
- else:
136
- reason = f"HTTP Request Parsing: HTTP Request with Script Undocumented ERROR: {self.error_message}"
137
- function_result = True
138
- error = True
139
- # If there's no error at all in HTTP Parsing, then it's fine HTTP Request
139
+
140
+ if self.request_bytes is None or not is_first_bytes_http_request(self.request_bytes):
141
+ error = "HTTP Request Parsing: Not HTTP request by first bytes."
142
+ is_http = False
140
143
  else:
141
- reason = "HTTP Request Parsing: Valid HTTP request"
142
- function_result = True
143
- error = False
144
+ error: str = str()
145
+
146
+ self.rfile = BytesIO(self.request_bytes)
147
+ self.raw_requestline = self.rfile.readline()
148
+ self.error_code = self.error_message = None
149
+ self.parse_request()
144
150
 
145
- return function_result, reason, error
151
+ # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values.
152
+ if hasattr(self, 'headers'):
153
+ # The "body" of request is in the 'Content-Length' key. If it exists in "headers" - get the body
154
+ if 'Content-Length' in self.headers.keys():
155
+ # "self.headers.get('Content-Length')" returns number in string format, "int" converts it to integer
156
+ self.content_length = int(self.headers.get('Content-Length'))
157
+ self.body = self.rfile.read(self.content_length)
158
+
159
+ # Examples:
160
+ # Getting path: self.path
161
+ # Getting Request Version: self.request_version
162
+ # Getting specific header: self.headers['host']
163
+
164
+ # If there's any error in HTTP parsing
165
+ if self.error_message:
166
+ # If error status is "BAD_REQUEST"
167
+ if self.error_message.startswith("Bad request"):
168
+ # If it's 'Bad request' this is not HTTP request, so we can
169
+ # continue the execution and parse the code as NON-HTTP Request.
170
+ # Currently, seen 'Bad request syntax' and 'Bad request version'.
171
+ error = f"HTTP Request Parsing: Not HTTP request: {self.error_message}"
172
+ is_http = False
173
+ else:
174
+ error = f"HTTP Request Parsing: HTTP Request with Script Undocumented ERROR: {self.error_message}"
175
+ is_http = False
176
+ # If there's no error at all in HTTP Parsing, then it's fine HTTP Request
177
+ else:
178
+ is_http = True
179
+
180
+ return self, is_http, error
146
181
 
147
182
 
148
183
  class FakeSocket:
149
184
  """
150
- FakeSocket is needed to parse HTTP Response. Socket object is needed for HTTPResponse class input.
185
+ FakeSocket mimics a socket object for parsing HTTP responses.
151
186
  """
152
187
  def __init__(self, response_bytes):
153
188
  self._file = BytesIO(response_bytes)
154
189
 
155
- def makefile(self, *args, **kwargs):
190
+ # noinspection PyUnusedLocal
191
+ def makefile(self, mode='rb', buffering=-1) -> BytesIO:
192
+ """
193
+ Mimics the socket's makefile method, returning the BytesIO object.
194
+ """
156
195
  return self._file
157
196
 
197
+ def fileno(self) -> int:
198
+ """
199
+ Provide a dummy file descriptor, as some code might call this.
200
+ """
201
+ raise OSError("File descriptor not available in FakeSocket")
202
+
158
203
 
159
204
  class HTTPResponseParse:
160
205
  def __init__(self, response_raw_bytes: bytes):
161
- self.error = None
162
206
  self.response_raw_bytes: bytes = response_raw_bytes
163
- # Assigning FakeSocket with response_raw_bytes.
164
- self.source = FakeSocket(self.response_raw_bytes)
165
207
 
166
- # Initializing HTTPResponse class with the FakeSocket with response_raw_bytes as input.
167
- self.response_raw_decoded = HTTPResponse(self.source)
208
+ self.error = None
209
+ self.source = None
210
+ self.response_raw_parsed = None
211
+ self.is_http: bool = False
168
212
 
169
- # Try to parse HTTP Response.
170
- try:
171
- self.response_raw_decoded.begin()
172
- # If there were problems with the status line.
173
- except http.client.BadStatusLine:
174
- self.error = "HTTP Response Parsing: Not a valid HTTP Response: Bad Status Line."
175
- pass
213
+ def parse(self):
214
+ if self.response_raw_bytes is None or not is_first_bytes_http_response(self.response_raw_bytes):
215
+ self.error = "HTTP Response Parsing: Not a valid HTTP Response by first bytes."
216
+ self.is_http = False
217
+ self.response_raw_parsed = None
218
+ else:
219
+ # Assigning FakeSocket with response_raw_bytes.
220
+ self.source = FakeSocket(self.response_raw_bytes)
176
221
 
177
- try:
178
- # If no exception was thrown, but there are some problems with headers.
179
- if self.response_raw_decoded.headers.defects:
222
+ # Initializing HTTPResponse class with the FakeSocket with response_raw_bytes as input.
223
+ # noinspection PyTypeChecker
224
+ self.response_raw_parsed = HTTPResponse(self.source)
225
+
226
+ # Try to parse HTTP Response.
227
+ try:
228
+ self.response_raw_parsed.begin()
229
+ self.is_http = True
230
+ # If there were problems with the status line.
231
+ except http.client.BadStatusLine:
232
+ self.error = "HTTP Response Parsing: Not a valid HTTP Response: Bad Status Line."
233
+ self.is_http = False
234
+
235
+ header_exists: bool = False
236
+ if (self.response_raw_parsed is not None and hasattr(self.response_raw_parsed, 'headers') and
237
+ self.response_raw_parsed.headers is not None):
238
+ header_exists = True
239
+
240
+ if header_exists and self.response_raw_parsed.headers.defects:
180
241
  self.error = f"HTTP Response Parsing: Not a valid HTTP Response: Some defects in headers: " \
181
- f"{self.response_raw_decoded.headers.defects}"
182
- # If the attribute of defects doesn't exist, probably the response wasn't parsed at all by the library,
183
- # Meaning, that the exception was already handled.
184
- except AttributeError:
185
- pass
242
+ f"{self.response_raw_parsed.headers.defects}"
243
+ self.is_http = False
186
244
 
187
- # Before checking for body, we need to make sure that ".headers" property exists, if not, return empty values
188
- try:
189
- # The "body" of response is in the 'Content-Length' key. If it exists in "headers" - get the body.
190
- if 'Content-Length' in self.response_raw_decoded.headers.keys():
191
- # "self.response_raw_decoded.headers.get('Content-Length')" returns number in string format,
192
- # "int" converts it to integer.
193
- self.response_raw_decoded.content_length = int(self.response_raw_decoded.headers.get('Content-Length'))
194
- # Basically we need to extract only the number of bytes specified in 'Content-Length' from the end
195
- # of the response that we received.
196
- # self.response_raw_bytes[-23:]
197
- self.response_raw_decoded.body = self.response_raw_bytes[-self.response_raw_decoded.content_length:]
198
- else:
199
- self.response_raw_decoded.content_length = None
200
- self.response_raw_decoded.body = None
201
- except Exception:
202
- self.response_raw_decoded.content_length = None
203
- self.response_raw_decoded.body = None
204
- pass
245
+ if self.is_http:
246
+ # Before checking for body, we need to make sure that ".headers" property exists,
247
+ # if not, return empty values.
248
+ self.response_raw_parsed.content_length = None
249
+ self.response_raw_parsed.body = None
250
+ if header_exists and 'Content-Length' in self.response_raw_parsed.headers.keys():
251
+ # The "body" of response is in the 'Content-Length' key. If it exists in "headers" - get the body.
252
+ # "self.response_raw_decoded.headers.get('Content-Length')" returns number in string format,
253
+ # "int" converts it to integer.
254
+ self.response_raw_parsed.content_length = int(self.response_raw_parsed.headers.get('Content-Length'))
255
+ # Basically we need to extract only the number of bytes specified in 'Content-Length' from the end
256
+ # of the response that we received.
257
+ # self.response_raw_bytes[-23:]
258
+ self.response_raw_parsed.body = self.response_raw_bytes[-self.response_raw_parsed.content_length:]
259
+
260
+ return self.response_raw_parsed, self.is_http, self.error
@@ -2,7 +2,12 @@ import ipaddress
2
2
  from typing import Union, Literal
3
3
 
4
4
 
5
- def is_ip_address(string_value: str, ip_type: Union[Literal['ipv4', 'ipv6'], None] = None) -> bool:
5
+ def is_ip_address(
6
+ string_value: str,
7
+ ip_type: Union[
8
+ Literal['ipv4', 'ipv6'],
9
+ None] = None
10
+ ) -> bool:
6
11
  """
7
12
  The function checks if the string is an IPv4 or IPv6 address.
8
13