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
|
@@ -24,22 +24,23 @@ def convert_der_x509_bytes_to_pem_string(certificate) -> str:
|
|
|
24
24
|
return ssl.DER_cert_to_PEM_cert(certificate)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def is_tls(
|
|
27
|
+
def is_tls(
|
|
28
|
+
client_socket,
|
|
29
|
+
timeout: float = None
|
|
30
|
+
) -> Optional[Tuple[str, str]]:
|
|
28
31
|
"""
|
|
29
32
|
Return protocol type of the incoming socket after 'accept()'.
|
|
30
33
|
:param client_socket: incoming socket after 'accept()'.
|
|
34
|
+
:param timeout: optional timeout for receiving/peeking the first bytes.
|
|
31
35
|
:return: tuple with content type, protocol type + version.
|
|
32
36
|
If the length of the first bytes is less than 3, return None.
|
|
33
37
|
"""
|
|
34
38
|
|
|
35
|
-
first_bytes = receiver.peek_first_bytes(client_socket, bytes_amount=3)
|
|
39
|
+
first_bytes = receiver.peek_first_bytes(client_socket, bytes_amount=3, timeout=timeout)
|
|
36
40
|
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Extract the content type and version
|
|
42
|
-
content_type, version_major, version_minor = first_bytes
|
|
41
|
+
# Sometimes only one byte is available, so we need to handle that case.
|
|
42
|
+
# convert to a tuple of ints, add three Nones, and keep only the first 3 items.
|
|
43
|
+
content_type, version_major, version_minor = (tuple(first_bytes) + (None, None, None))[:3]
|
|
43
44
|
|
|
44
45
|
# Map TLS content types to their string representation.
|
|
45
46
|
content_type_map = {
|
|
@@ -52,19 +53,19 @@ def is_tls(client_socket) -> Optional[Tuple[str, str]]:
|
|
|
52
53
|
|
|
53
54
|
# Map TLS version bytes to their string representation.
|
|
54
55
|
version_map = {
|
|
55
|
-
(0x03, 0x00): "
|
|
56
|
-
(0x03, 0x01): "
|
|
57
|
-
(0x03, 0x02): "
|
|
58
|
-
(0x03, 0x03): "
|
|
56
|
+
(0x03, 0x00): "SSLv3.0",
|
|
57
|
+
(0x03, 0x01): "TLSv1.0",
|
|
58
|
+
(0x03, 0x02): "TLSv1.1",
|
|
59
|
+
(0x03, 0x03): "TLSv1.2/1.3"
|
|
59
60
|
# Remember, you can't definitively differentiate 1.2 and 1.3 just from these bytes
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
# Get the tuple of the type and version as strings.
|
|
63
|
-
tls_content_and_version_tuple: tuple
|
|
64
|
+
tls_content_and_version_tuple: tuple[str, str] = \
|
|
64
65
|
content_type_map.get(content_type), version_map.get((version_major, version_minor))
|
|
65
66
|
|
|
66
67
|
# If both parts of the tuple are not None, return the protocol type.
|
|
67
|
-
if tls_content_and_version_tuple[0]
|
|
68
|
+
if tls_content_and_version_tuple[0]:
|
|
68
69
|
return tls_content_and_version_tuple
|
|
69
70
|
else:
|
|
70
71
|
return None
|
|
@@ -1,23 +1,154 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
import multiprocessing
|
|
2
3
|
|
|
3
|
-
from
|
|
4
|
+
from ..loggingw import loggingw
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
LOGGER_NAME: str = 'statistics'
|
|
8
|
+
STATISTICS_HEADER: str = (
|
|
9
|
+
'request_time_sent,thread_id,engine,source_host,source_ip,tls,protocol,protocol2,protocol3,dest_port,host,path,command,status_code,request_size_bytes,'
|
|
10
|
+
'response_size_bytes,file_path,process_cmd,action,error')
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
|
|
13
|
+
class StatisticsCSVWriter(loggingw.CsvLogger):
|
|
14
|
+
"""
|
|
15
|
+
Class to write statistics to CSV file.
|
|
16
|
+
This can be initiated at the main, and then passed to the thread worker function.
|
|
17
|
+
"""
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
logger_name: str = LOGGER_NAME,
|
|
21
|
+
directory_path: str = None,
|
|
22
|
+
log_queue: multiprocessing.Queue = None,
|
|
23
|
+
add_queue_handler_start_listener_multiprocessing: bool = False,
|
|
24
|
+
add_queue_handler_no_listener_multiprocessing: bool = False
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Initialize the StatisticsCSVWriter with the directory path for the statistics CSV file.
|
|
28
|
+
:param directory_path: str, the directory path where the statistics CSV file will be created.
|
|
29
|
+
:param log_queue: multiprocessing.Queue, the queue to use for logging in multiprocessing.
|
|
30
|
+
:param add_queue_handler_start_listener_multiprocessing: bool, whether to add a queue handler that will use
|
|
31
|
+
the 'logger_queue' and start the queue listener with the same 'logger_queue' for multiprocessing.
|
|
32
|
+
:param add_queue_handler_no_listener_multiprocessing: bool, whether to add a queue handler that will use
|
|
33
|
+
the 'logger_queue' but will not start the queue listener for multiprocessing. This is useful when you
|
|
34
|
+
already started the queue listener and want to add more handlers to the logger without
|
|
35
|
+
starting a new listener.
|
|
36
|
+
|
|
37
|
+
If you don't set any of 'add_queue_handler_start_listener_multiprocessing' or
|
|
38
|
+
'add_queue_handler_no_listener_multiprocessing', the logger will be created without a queue handler.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
super().__init__(
|
|
42
|
+
logger_name=logger_name,
|
|
43
|
+
directory_path=directory_path,
|
|
44
|
+
log_queue=log_queue,
|
|
45
|
+
add_queue_handler_start_listener_multiprocessing=add_queue_handler_start_listener_multiprocessing,
|
|
46
|
+
add_queue_handler_no_listener_multiprocessing=add_queue_handler_no_listener_multiprocessing,
|
|
47
|
+
custom_header=STATISTICS_HEADER
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def write_row(
|
|
51
|
+
self,
|
|
52
|
+
thread_id: str,
|
|
53
|
+
engine: str,
|
|
54
|
+
source_host: str,
|
|
55
|
+
source_ip: str,
|
|
56
|
+
host: str,
|
|
57
|
+
tls_type: str,
|
|
58
|
+
tls_version: str,
|
|
59
|
+
protocol: str,
|
|
60
|
+
protocol2: str,
|
|
61
|
+
protocol3: str,
|
|
62
|
+
dest_port: str,
|
|
63
|
+
path: str,
|
|
64
|
+
status_code: str,
|
|
65
|
+
command: str,
|
|
66
|
+
request_size_bytes: str,
|
|
67
|
+
response_size_bytes: str,
|
|
68
|
+
recorded_file_path: str = None,
|
|
69
|
+
process_cmd: str = None,
|
|
70
|
+
error: str = None,
|
|
71
|
+
action: str = None,
|
|
72
|
+
timestamp=None,
|
|
73
|
+
):
|
|
74
|
+
if not timestamp:
|
|
75
|
+
timestamp = datetime.datetime.now()
|
|
76
|
+
|
|
77
|
+
if not tls_type and not tls_version:
|
|
78
|
+
tls_info = ''
|
|
79
|
+
else:
|
|
80
|
+
tls_info = f'{tls_type}|{tls_version}'
|
|
81
|
+
|
|
82
|
+
row_of_cols: list = [
|
|
83
|
+
timestamp,
|
|
84
|
+
thread_id,
|
|
85
|
+
engine,
|
|
86
|
+
source_host,
|
|
87
|
+
source_ip,
|
|
88
|
+
tls_info,
|
|
89
|
+
protocol,
|
|
90
|
+
protocol2,
|
|
91
|
+
protocol3,
|
|
92
|
+
dest_port,
|
|
93
|
+
host,
|
|
94
|
+
path,
|
|
95
|
+
command,
|
|
96
|
+
status_code,
|
|
97
|
+
request_size_bytes,
|
|
98
|
+
response_size_bytes,
|
|
99
|
+
recorded_file_path,
|
|
100
|
+
process_cmd,
|
|
101
|
+
action,
|
|
102
|
+
error
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
super().write(row_of_cols)
|
|
106
|
+
|
|
107
|
+
def write_accept_error(
|
|
108
|
+
self,
|
|
109
|
+
engine: str,
|
|
110
|
+
source_ip: str,
|
|
111
|
+
source_host: str,
|
|
112
|
+
error_message: str,
|
|
113
|
+
dest_port: str,
|
|
114
|
+
host: str,
|
|
115
|
+
process_name: str,
|
|
116
|
+
thread_id: str = str()
|
|
117
|
+
):
|
|
118
|
+
"""
|
|
119
|
+
Write the error message to the statistics CSV file.
|
|
120
|
+
This is used for easier execution, since most of the parameters will be empty on accept.
|
|
121
|
+
|
|
122
|
+
:param engine: string, engine name.
|
|
123
|
+
:param source_ip: string, source IP address.
|
|
124
|
+
:param source_host: string, source host name.
|
|
125
|
+
:param error_message: string, error message.
|
|
126
|
+
:param dest_port: string, destination port.
|
|
127
|
+
:param host: string, host, the domain or IP address.
|
|
128
|
+
:param process_name: process name, the command line of the process.
|
|
129
|
+
:param thread_id: integer, the id of the thread.
|
|
130
|
+
:return:
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
self.write_row(
|
|
134
|
+
thread_id=thread_id,
|
|
135
|
+
engine=engine,
|
|
136
|
+
source_host=source_host,
|
|
137
|
+
source_ip=source_ip,
|
|
138
|
+
tls_type='',
|
|
139
|
+
tls_version='',
|
|
140
|
+
protocol='',
|
|
141
|
+
protocol2='',
|
|
142
|
+
protocol3='',
|
|
143
|
+
dest_port=dest_port,
|
|
144
|
+
host=host,
|
|
145
|
+
path='',
|
|
146
|
+
status_code='',
|
|
147
|
+
command='',
|
|
148
|
+
request_size_bytes='',
|
|
149
|
+
response_size_bytes='',
|
|
150
|
+
recorded_file_path='',
|
|
151
|
+
process_cmd=process_name,
|
|
152
|
+
action='client_accept',
|
|
153
|
+
error=error_message
|
|
17
154
|
)
|
|
18
|
-
except UnboundLocalError:
|
|
19
|
-
pass
|
|
20
|
-
except Exception:
|
|
21
|
-
message = "Undocumented exception after accept on building statistics."
|
|
22
|
-
print_api(message, error_type=True, logger_method='error', traceback_string=True, oneline=True, **print_kwargs)
|
|
23
|
-
pass
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
from .. import filesystem, web, process
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Define paths and configuration
|
|
8
|
+
DEFAULT_INSTALLATION_PATH: str = 'C:\\Sysmon'
|
|
9
|
+
SYSMON_FILE_NAME: str = 'Sysmon.exe'
|
|
10
|
+
SYSINTERNALS_SYSMON_URL: str = 'https://download.sysinternals.com/files/Sysmon.zip'
|
|
11
|
+
SYSMON_CONFIG_FILE_NAME: str = 'sysmonconfig.xml'
|
|
12
|
+
SYSMON_CONFIG_FILE_PATH: str = os.path.join(DEFAULT_INSTALLATION_PATH, SYSMON_CONFIG_FILE_NAME)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConfigFileNotFoundError(Exception):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SymonExecutableNotFoundError(Exception):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SysmonAlreadyRunningError(Exception):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def download_sysmon(installation_path: str = None):
|
|
28
|
+
"""
|
|
29
|
+
Install Sysmon on the system.
|
|
30
|
+
|
|
31
|
+
:param installation_path: string, full path where to put the Sysmon executable.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
if not installation_path:
|
|
35
|
+
installation_path = DEFAULT_INSTALLATION_PATH
|
|
36
|
+
|
|
37
|
+
# Check if the file exists
|
|
38
|
+
if not os.path.exists(installation_path):
|
|
39
|
+
filesystem.create_directory(installation_path)
|
|
40
|
+
|
|
41
|
+
web.download_and_extract_file(SYSINTERNALS_SYSMON_URL, installation_path)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def is_sysmon_running():
|
|
45
|
+
"""
|
|
46
|
+
Check if Sysmon is running.
|
|
47
|
+
|
|
48
|
+
:return: boolean, True if Sysmon is running, False otherwise.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
process_list: list = process.get_running_processes_by_cmdline_pattern(
|
|
52
|
+
pattern=SYSMON_FILE_NAME, first=True, cmdline_case_insensitive=True)
|
|
53
|
+
|
|
54
|
+
if process_list:
|
|
55
|
+
return True
|
|
56
|
+
else:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def start_as_service(
|
|
61
|
+
installation_path: str = None,
|
|
62
|
+
config_file_path: str = None,
|
|
63
|
+
use_config_in_same_directory: bool = False,
|
|
64
|
+
download_sysmon_if_not_found: bool = False,
|
|
65
|
+
skip_if_running: bool = False
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
Start Sysmon as a service. Besides starting, it installs itself as a service, meaning that on the next boot,
|
|
69
|
+
it will start automatically.
|
|
70
|
+
|
|
71
|
+
:param installation_path: string, full path where to put the Sysmon executable.
|
|
72
|
+
:param config_file_path: string, full path to the configuration file.
|
|
73
|
+
:param use_config_in_same_directory: boolean, if True, the function will use the configuration file in the same
|
|
74
|
+
directory as the Sysmon executable.
|
|
75
|
+
:param download_sysmon_if_not_found: boolean, if True, the function will download Sysmon if it is not
|
|
76
|
+
found in the 'installation_path'.
|
|
77
|
+
:param skip_if_running: boolean,
|
|
78
|
+
True, the function will not start Sysmon if it is already running.
|
|
79
|
+
False, the function will raise 'SysmonAlreadyRunningError' exception if it is already running.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# Check if sysmon already running.
|
|
83
|
+
if is_sysmon_running():
|
|
84
|
+
if skip_if_running:
|
|
85
|
+
return
|
|
86
|
+
else:
|
|
87
|
+
raise SysmonAlreadyRunningError("Sysmon is already running.")
|
|
88
|
+
|
|
89
|
+
if config_file_path and use_config_in_same_directory:
|
|
90
|
+
raise ValueError("You cannot use both 'config_file_path' and 'use_config_in_same_directory'.")
|
|
91
|
+
|
|
92
|
+
if use_config_in_same_directory:
|
|
93
|
+
config_file_path = SYSMON_CONFIG_FILE_PATH
|
|
94
|
+
|
|
95
|
+
if config_file_path:
|
|
96
|
+
# Check if the file exists
|
|
97
|
+
if not os.path.exists(config_file_path):
|
|
98
|
+
raise ConfigFileNotFoundError(f"Configuration file '{config_file_path}' not found.")
|
|
99
|
+
|
|
100
|
+
if not installation_path:
|
|
101
|
+
installation_path = DEFAULT_INSTALLATION_PATH
|
|
102
|
+
|
|
103
|
+
sysmon_file_path: str = os.path.join(installation_path, SYSMON_FILE_NAME)
|
|
104
|
+
|
|
105
|
+
# Check if the file exists
|
|
106
|
+
if not os.path.exists(sysmon_file_path):
|
|
107
|
+
if download_sysmon_if_not_found:
|
|
108
|
+
download_sysmon(installation_path)
|
|
109
|
+
else:
|
|
110
|
+
raise SymonExecutableNotFoundError(f"Sysmon executable '{sysmon_file_path}' not found.")
|
|
111
|
+
|
|
112
|
+
# Start Sysmon as a service.
|
|
113
|
+
if config_file_path:
|
|
114
|
+
subprocess.run([sysmon_file_path, '-accepteula', '-i', config_file_path])
|
|
115
|
+
else:
|
|
116
|
+
subprocess.run([sysmon_file_path, '-accepteula', '-i'])
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def stop_service(installation_path: str = None):
|
|
120
|
+
"""
|
|
121
|
+
Stop Sysmon service.
|
|
122
|
+
|
|
123
|
+
:param installation_path: string, full path where to put the Sysmon executable.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
if not installation_path:
|
|
127
|
+
installation_path = DEFAULT_INSTALLATION_PATH
|
|
128
|
+
|
|
129
|
+
sysmon_file_path: str = os.path.join(installation_path, SYSMON_FILE_NAME)
|
|
130
|
+
|
|
131
|
+
# Check if the file exists
|
|
132
|
+
if not os.path.exists(sysmon_file_path):
|
|
133
|
+
raise SymonExecutableNotFoundError(f"Sysmon executable '{sysmon_file_path}' not found.")
|
|
134
|
+
|
|
135
|
+
# Stop Sysmon service.
|
|
136
|
+
subprocess.run([sysmon_file_path, '-u'])
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def change_config_on_the_fly(config_file_path: str, installation_path: str = None):
|
|
140
|
+
"""
|
|
141
|
+
Change the Sysmon configuration on the fly.
|
|
142
|
+
|
|
143
|
+
:param config_file_path: string, full path to the configuration file.
|
|
144
|
+
:param installation_path: string, full path where to put the Sysmon executable.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
if not installation_path:
|
|
148
|
+
installation_path = DEFAULT_INSTALLATION_PATH
|
|
149
|
+
|
|
150
|
+
sysmon_file_path: str = os.path.join(installation_path, SYSMON_FILE_NAME)
|
|
151
|
+
|
|
152
|
+
# Check if the file exists
|
|
153
|
+
if not os.path.exists(sysmon_file_path):
|
|
154
|
+
raise SymonExecutableNotFoundError(f"Sysmon executable '{sysmon_file_path}' not found.")
|
|
155
|
+
|
|
156
|
+
# Change the configuration on the fly.
|
|
157
|
+
subprocess.run([sysmon_file_path, '-c', config_file_path])
|
|
@@ -4,23 +4,64 @@ import subprocess
|
|
|
4
4
|
import shutil
|
|
5
5
|
import time
|
|
6
6
|
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
7
9
|
from ..print_api import print_api
|
|
8
|
-
from .. import
|
|
10
|
+
from ..permissions import ubuntu_permissions
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
9
14
|
|
|
10
15
|
|
|
11
|
-
def install_packages(
|
|
16
|
+
def install_packages(
|
|
17
|
+
package_list: list[str],
|
|
18
|
+
timeout_seconds: int = 0,
|
|
19
|
+
):
|
|
12
20
|
"""
|
|
13
21
|
Function installs a package using apt-get.
|
|
14
22
|
:param package_list: list of strings, package names to install.
|
|
23
|
+
:param timeout_seconds: int, if the 'apt-get' command is busy at the moment, the function will wait for
|
|
24
|
+
'timeout_seconds' seconds before raising an error.
|
|
25
|
+
'-1' means wait indefinitely.
|
|
15
26
|
:return:
|
|
16
27
|
"""
|
|
17
28
|
|
|
18
29
|
# Construct the command with the package list
|
|
19
|
-
command = ["sudo", "apt
|
|
30
|
+
command = ["sudo", "apt", "install", "-y"] + package_list
|
|
31
|
+
|
|
32
|
+
if timeout_seconds != 0:
|
|
33
|
+
command.extend(["-o", f"DPkg::Lock::Timeout={str(timeout_seconds)}"])
|
|
20
34
|
|
|
21
35
|
subprocess.check_call(command)
|
|
22
36
|
|
|
23
37
|
|
|
38
|
+
def remove_packages(
|
|
39
|
+
package_list: list[str],
|
|
40
|
+
remove_config_files: bool = False,
|
|
41
|
+
remove_dependencies: bool = False,
|
|
42
|
+
):
|
|
43
|
+
"""
|
|
44
|
+
Function removes a list of packages.
|
|
45
|
+
:param package_list: list of strings, package names to remove. Regular removal is through 'apt remove'.
|
|
46
|
+
:param remove_config_files: bool, if True, the config files will be removed also through 'apt purge'.
|
|
47
|
+
:param remove_dependencies: bool, if True, the dependencies will be removed also through 'apt autoremove'.
|
|
48
|
+
:return:
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# Construct the command with the package list
|
|
52
|
+
command = ["sudo", "apt", "remove", "-y"] + package_list
|
|
53
|
+
|
|
54
|
+
# If remove_config_files is True, add 'purge' to the command
|
|
55
|
+
if remove_config_files:
|
|
56
|
+
command.insert(2, "purge")
|
|
57
|
+
|
|
58
|
+
subprocess.check_call(command)
|
|
59
|
+
|
|
60
|
+
# If remove_dependencies is True, remove the dependencies
|
|
61
|
+
if remove_dependencies:
|
|
62
|
+
subprocess.check_call(["sudo", "apt", "autoremove", "-y"])
|
|
63
|
+
|
|
64
|
+
|
|
24
65
|
def is_package_installed(package: str) -> bool:
|
|
25
66
|
"""
|
|
26
67
|
Function checks if a package is installed.
|
|
@@ -74,7 +115,7 @@ def update_system_packages():
|
|
|
74
115
|
Function updates the system packages.
|
|
75
116
|
:return:
|
|
76
117
|
"""
|
|
77
|
-
subprocess.check_call(['sudo', 'apt
|
|
118
|
+
subprocess.check_call(['sudo', 'apt', 'update'])
|
|
78
119
|
|
|
79
120
|
|
|
80
121
|
def upgrade_system_packages(apt_update: bool = True):
|
|
@@ -88,7 +129,7 @@ def upgrade_system_packages(apt_update: bool = True):
|
|
|
88
129
|
if apt_update:
|
|
89
130
|
update_system_packages()
|
|
90
131
|
|
|
91
|
-
subprocess.check_call(['sudo', 'apt
|
|
132
|
+
subprocess.check_call(['sudo', 'apt', 'upgrade', '-y'])
|
|
92
133
|
|
|
93
134
|
|
|
94
135
|
def is_service_running(service_name: str, user_mode: bool = False, return_false_on_error: bool = False) -> bool:
|
|
@@ -175,28 +216,25 @@ def start_service(service_name: str, sudo: bool = False, user_mode: bool = False
|
|
|
175
216
|
def start_enable_service_check_availability(
|
|
176
217
|
service_name: str,
|
|
177
218
|
wait_time_seconds: float = 30,
|
|
178
|
-
exit_on_error: bool = True,
|
|
179
219
|
start_service_bool: bool = True,
|
|
180
220
|
enable_service_bool: bool = True,
|
|
181
221
|
check_service_running: bool = True,
|
|
182
222
|
user_mode: bool = False,
|
|
183
|
-
sudo: bool = True
|
|
184
|
-
|
|
185
|
-
):
|
|
223
|
+
sudo: bool = True
|
|
224
|
+
) -> int:
|
|
186
225
|
"""
|
|
187
226
|
Function starts and enables a service and checks its availability.
|
|
188
227
|
|
|
189
228
|
:param service_name: str, the service name.
|
|
190
229
|
:param wait_time_seconds: float, the time to wait after starting the service before checking the service
|
|
191
230
|
availability.
|
|
192
|
-
:param exit_on_error: bool, if True, the function will exit the program if the service is not available.
|
|
193
231
|
:param start_service_bool: bool, if True, the service will be started.
|
|
194
232
|
:param enable_service_bool: bool, if True, the service will be enabled.
|
|
195
233
|
:param check_service_running: bool, if True, the function will check if the service is running.
|
|
196
234
|
:param user_mode: bool, if True, the service will be started and enabled in user mode.
|
|
197
235
|
:param sudo: bool, if True, the command will be executed with sudo.
|
|
198
|
-
|
|
199
|
-
:return:
|
|
236
|
+
|
|
237
|
+
:return: int, 0 if the service is running, 1 if the service is not running.
|
|
200
238
|
"""
|
|
201
239
|
|
|
202
240
|
if not start_service_bool and not enable_service_bool:
|
|
@@ -209,18 +247,19 @@ def start_enable_service_check_availability(
|
|
|
209
247
|
enable_service(service_name, user_mode=user_mode,sudo=sudo)
|
|
210
248
|
|
|
211
249
|
if check_service_running:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
250
|
+
print(f"Waiting up to {str(wait_time_seconds)} seconds for the program to start.")
|
|
251
|
+
count: int = 0
|
|
252
|
+
while not is_service_running(service_name, user_mode=user_mode) and count < wait_time_seconds:
|
|
253
|
+
count += 1
|
|
254
|
+
time.sleep(1)
|
|
216
255
|
|
|
217
256
|
if not is_service_running(service_name, user_mode=user_mode):
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if exit_on_error:
|
|
221
|
-
sys.exit(1)
|
|
257
|
+
console.print(f"[{service_name}] service failed to start.", style='red', markup=False)
|
|
258
|
+
return 1
|
|
222
259
|
else:
|
|
223
|
-
|
|
260
|
+
console.print(f"[{service_name}] service is running.", style='green', markup=False)
|
|
261
|
+
|
|
262
|
+
return 0
|
|
224
263
|
|
|
225
264
|
|
|
226
265
|
def add_path_to_bashrc(as_regular_user: bool = False):
|
|
@@ -231,11 +270,11 @@ def add_path_to_bashrc(as_regular_user: bool = False):
|
|
|
231
270
|
|
|
232
271
|
if as_regular_user:
|
|
233
272
|
# Get the current non-sudo user
|
|
234
|
-
with
|
|
273
|
+
with ubuntu_permissions.temporary_regular_permissions():
|
|
235
274
|
current_non_sudo_user = os.getlogin()
|
|
236
275
|
|
|
237
276
|
# Get the home path of the current non-sudo user
|
|
238
|
-
user_bashrc_path =
|
|
277
|
+
user_bashrc_path = ubuntu_permissions.expand_user_path(current_non_sudo_user, home_path_bashrc)
|
|
239
278
|
else:
|
|
240
279
|
user_bashrc_path = os.path.expanduser(home_path_bashrc)
|
|
241
280
|
|
|
@@ -258,11 +297,11 @@ def add_line_to_bashrc(line: str, as_regular_user: bool = False):
|
|
|
258
297
|
|
|
259
298
|
if as_regular_user:
|
|
260
299
|
# Get the current non-sudo user
|
|
261
|
-
with
|
|
300
|
+
with ubuntu_permissions.temporary_regular_permissions():
|
|
262
301
|
current_non_sudo_user = os.getlogin()
|
|
263
302
|
|
|
264
303
|
# Get the home path of the current non-sudo user
|
|
265
|
-
user_bashrc_path =
|
|
304
|
+
user_bashrc_path = ubuntu_permissions.expand_user_path(current_non_sudo_user, home_path_bashrc)
|
|
266
305
|
else:
|
|
267
306
|
user_bashrc_path = os.path.expanduser(home_path_bashrc)
|
|
268
307
|
|
|
@@ -293,7 +332,7 @@ def get_command_execution_as_sudo_executer(command: str, add_bash_exec: bool = F
|
|
|
293
332
|
|
|
294
333
|
command = command.replace('sudo ', '').strip()
|
|
295
334
|
|
|
296
|
-
sudo_executer_username: str =
|
|
335
|
+
sudo_executer_username: str = ubuntu_permissions.get_sudo_executer_username()
|
|
297
336
|
|
|
298
337
|
if sudo_executer_username:
|
|
299
338
|
if add_bash_exec:
|