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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/appointment_management.py +5 -3
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/dicts.py +12 -0
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/list_of_dicts.py +69 -5
- atomicshop/basics/lists.py +14 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/package_module.py +10 -0
- atomicshop/basics/strings.py +160 -7
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -4
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +12 -7
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +98 -0
- atomicshop/diff_check.py +340 -40
- atomicshop/dns.py +128 -12
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/const.py +38 -0
- atomicshop/etws/providers.py +21 -0
- atomicshop/etws/sessions.py +43 -0
- atomicshop/etws/trace.py +168 -0
- atomicshop/etws/traces/trace_dns.py +162 -0
- atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +222 -24
- atomicshop/file_io/docxs.py +35 -18
- atomicshop/file_io/file_io.py +35 -19
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +864 -293
- atomicshop/get_process_list.py +133 -0
- atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -74
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +257 -166
- atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
- atomicshop/monitor/change_monitor.py +96 -120
- atomicshop/monitor/checks/dns.py +139 -70
- atomicshop/monitor/checks/file.py +77 -0
- atomicshop/monitor/checks/network.py +81 -77
- atomicshop/monitor/checks/process_running.py +33 -34
- atomicshop/monitor/checks/url.py +94 -0
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +205 -0
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -41
- atomicshop/process.py +63 -17
- atomicshop/process_poller/__init__.py +0 -0
- atomicshop/process_poller/pollers/__init__.py +0 -0
- atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
- atomicshop/process_poller/process_pool.py +207 -0
- atomicshop/process_poller/simple_process_pool.py +311 -0
- atomicshop/process_poller/tracer_base.py +45 -0
- atomicshop/process_poller/tracers/__init__.py +0 -0
- atomicshop/process_poller/tracers/event_log.py +46 -0
- atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/question_answer_engine.py +2 -2
- atomicshop/scheduling.py +24 -5
- atomicshop/sound.py +4 -2
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/startup/__init__.py +0 -0
- atomicshop/startup/win/__init__.py +0 -0
- atomicshop/startup/win/startup_folder.py +53 -0
- atomicshop/startup/win/task_scheduler.py +119 -0
- atomicshop/system_resource_monitor.py +61 -46
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/timer.py +30 -11
- atomicshop/urls.py +41 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +110 -25
- atomicshop/web_apis/__init__.py +0 -0
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
- atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
- atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
- atomicshop/wrappers/githubw.py +583 -51
- atomicshop/wrappers/loggingw/consts.py +49 -0
- atomicshop/wrappers/loggingw/filters.py +102 -0
- atomicshop/wrappers/loggingw/formatters.py +58 -71
- atomicshop/wrappers/loggingw/handlers.py +459 -40
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +1010 -178
- atomicshop/wrappers/loggingw/reading.py +344 -19
- atomicshop/wrappers/mongodbw/__init__.py +0 -0
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +81 -0
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/psutilw/psutilw.py +9 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/console.py +34 -0
- atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
- atomicshop/wrappers/pywin32w/winshell.py +19 -0
- atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +500 -173
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +157 -0
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/addons/process_list/compile.cmd +0 -2
- atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
- atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/etw/dns_trace.py +0 -118
- atomicshop/etw/etw.py +0 -61
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -240
- atomicshop/monitor/checks/hash.py +0 -44
- atomicshop/monitor/checks/hash_checks/file.py +0 -55
- atomicshop/monitor/checks/hash_checks/url.py +0 -62
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -110
- atomicshop/process_poller.py +0 -237
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/loggingw/checks.py +0 -20
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.11.47.dist-info/RECORD +0 -251
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
- /atomicshop/{archiver → etws}/__init__.py +0 -0
- /atomicshop/{etw → etws/traces}/__init__.py +0 -0
- /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
- /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {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
|
|
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
|
|
13
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
75
|
+
processes = self.callback_output.data
|
|
55
76
|
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
# Clear the callback output list, or it will be appended each time.
|
|
78
|
+
self.callback_output.data = list()
|
|
58
79
|
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
if sort_by:
|
|
81
|
+
processes = list_of_dicts.sort_by_keys(processes, key_list=[sort_by])
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
if as_dict:
|
|
84
|
+
processes = convert_processes_to_dict(processes)
|
|
64
85
|
|
|
65
|
-
|
|
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":
|
|
105
|
-
"cmdline":
|
|
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":
|
|
111
|
-
"cmdline":
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
self.
|
|
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
|
-
#
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
#
|
|
80
|
-
|
|
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
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
167
|
-
self.
|
|
208
|
+
self.error = None
|
|
209
|
+
self.source = None
|
|
210
|
+
self.response_raw_parsed = None
|
|
211
|
+
self.is_http: bool = False
|
|
168
212
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
self.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
178
|
-
#
|
|
179
|
-
|
|
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.
|
|
182
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
atomicshop/ip_addresses.py
CHANGED
|
@@ -2,7 +2,12 @@ import ipaddress
|
|
|
2
2
|
from typing import Union, Literal
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def is_ip_address(
|
|
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
|
|