atomicshop 2.15.11__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 (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,80 @@
1
+ import json
2
+ import subprocess
3
+ from typing import List, Literal
4
+
5
+
6
+ def get_interface_ips(
7
+ interface_name: str,
8
+ ip_type: Literal["virtual", "dynamic", "all"] = "virtual"
9
+ ) -> List[str]:
10
+ """
11
+ Return IPv4 addresses on an interface, filtered by 'mode'.
12
+
13
+ ip_type:
14
+ - "virtual": only static/virtual IPs (PrefixOrigin != 'Dhcp')
15
+ - "dynamic": only DHCP IPs (PrefixOrigin == 'Dhcp')
16
+ - "all": all IPv4 IPs on the interface
17
+
18
+ If the interface does not exist or has no IPv4 addresses, returns [].
19
+ """
20
+
21
+ ps_script = f"""
22
+ try {{
23
+ Get-NetIPAddress -InterfaceAlias "{interface_name}" -AddressFamily IPv4 |
24
+ Select-Object IPAddress,
25
+ @{{
26
+ Name = 'PrefixOrigin';
27
+ Expression = {{ [string]$_.PrefixOrigin }}
28
+ }} |
29
+ ConvertTo-Json -Depth 3
30
+ }} catch {{
31
+ # Return empty JSON array if nothing found / interface missing
32
+ '[]'
33
+ }}
34
+ """
35
+
36
+ try:
37
+ result = subprocess.run(
38
+ ["powershell", "-NoProfile", "-Command", ps_script],
39
+ capture_output=True,
40
+ text=True,
41
+ check=True
42
+ )
43
+ except subprocess.CalledProcessError as e:
44
+ # If anything unexpected happens, raise a clearer error
45
+ msg = (e.stderr or e.stdout or "").strip()
46
+ raise RuntimeError(f"PowerShell Get-NetIPAddress failed: {msg}") from e
47
+
48
+ stdout = result.stdout.strip()
49
+ if not stdout:
50
+ return []
51
+
52
+ # At this point stdout should be valid JSON (list or single object)
53
+ data = json.loads(stdout)
54
+
55
+ if isinstance(data, dict):
56
+ data = [data]
57
+
58
+ ips: List[str] = []
59
+ ip_type = ip_type.lower()
60
+
61
+ for entry in data:
62
+ ip = entry.get("IPAddress")
63
+ origin_raw = entry.get("PrefixOrigin", "")
64
+ origin = str(origin_raw).lower()
65
+
66
+ if not ip:
67
+ continue
68
+
69
+ if ip_type == "virtual":
70
+ if origin != "dhcp":
71
+ ips.append(ip)
72
+ elif ip_type == "dynamic":
73
+ if origin == "dhcp":
74
+ ips.append(ip)
75
+ elif ip_type == "all":
76
+ ips.append(ip)
77
+ else:
78
+ raise ValueError(f"Unsupported mode: {ip_type!r}")
79
+
80
+ return ips
@@ -1,7 +1,10 @@
1
- import psutil
1
+ import os
2
2
  import time
3
3
 
4
+ import psutil
5
+
4
6
  from ...print_api import print_api
7
+ from ..ctyping import file_details_winapi
5
8
 
6
9
 
7
10
  def wait_for_process(pid: int):
@@ -43,3 +46,36 @@ def kill_process_by_pid(pid: int, print_kwargs: dict = None):
43
46
  except psutil.TimeoutExpired:
44
47
  # print(f"Process {pid} did not terminate in time.")
45
48
  pass
49
+
50
+
51
+ def get_running_processes_with_exe_info() -> list[dict]:
52
+ """
53
+ Retrieve information about all running processes on the system.
54
+ """
55
+ processes_info: list[dict] = []
56
+
57
+ for proc in psutil.process_iter(attrs=["pid", "name", "exe"]):
58
+ try:
59
+ pid = proc.info["pid"]
60
+ name = proc.info["name"]
61
+ exe = proc.info["exe"]
62
+
63
+ if exe and os.path.isfile(exe):
64
+ # Get file properties
65
+ file_properties = file_details_winapi.get_file_properties(exe)
66
+
67
+ # Add process info to the list
68
+ processes_info.append({
69
+ "PID": pid,
70
+ "Name": name,
71
+ "FilePath": exe,
72
+ "FileDescription": file_properties["FileDescription"],
73
+ "FileVersion": file_properties["FileVersion"],
74
+ "ProductName": file_properties["ProductName"],
75
+ "ProductVersion": file_properties["ProductVersion"],
76
+ })
77
+ except (psutil.AccessDenied, psutil.NoSuchProcess, psutil.ZombieProcess):
78
+ # Skip processes that cannot be accessed
79
+ continue
80
+
81
+ return processes_info
@@ -0,0 +1,85 @@
1
+ from typing import Union
2
+ import shlex
3
+ import socket
4
+
5
+ import psutil
6
+
7
+ from ... import networks
8
+
9
+
10
+ def get_process_using_port(ip_port: str) -> Union[dict, None]:
11
+ """
12
+ Function to find the process using the port.
13
+ :param ip_port: string, Listening IP and port number. Example: '192.168.0.1:443'
14
+ :return: dict['pid', 'name', 'cmdline'] or None.
15
+ """
16
+
17
+ ip_address, port = ip_port.split(':')
18
+ port = int(port)
19
+
20
+ for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
21
+ try:
22
+ connections = proc.connections(kind='inet')
23
+ for conn in connections:
24
+ # if conn.laddr.port == port:
25
+ # Status LISTEN is for TCP sockets and NONE is for UDP sockets.
26
+ # Sometimes after socket close, the port will be in TIME_WAIT state.
27
+ if (conn.laddr.port == port and (conn.status == 'LISTEN' or conn.status == 'NONE')) and conn.laddr.ip == ip_address:
28
+ cmdline = proc.info['cmdline']
29
+ if not cmdline:
30
+ cmdline = '<EMPTY: TRY RUNNING AS ADMIN>'
31
+ else:
32
+ cmdline = shlex.join(cmdline)
33
+ return {
34
+ 'pid': proc.info['pid'],
35
+ 'name': proc.info['name'],
36
+ 'cmdline': cmdline
37
+ }
38
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
39
+ pass
40
+ return None
41
+
42
+
43
+ def get_processes_using_port_list(ips_ports: list) -> Union[dict, None]:
44
+ """
45
+ Function to find the process using the port.
46
+ :param ips_ports: List of listening ips and port numbers. Example:
47
+ ['192.168.0.1:443', '192.168.0.2:443']
48
+ :return: dict[port: {'pid', 'name', 'cmdline'}] or None.
49
+ """
50
+ port_process_map = {}
51
+ for ip_port in ips_ports:
52
+ process_info = get_process_using_port(ip_port)
53
+ if process_info:
54
+ port_process_map[ip_port] = process_info
55
+
56
+ return port_process_map
57
+
58
+
59
+ def get_default_connection_name() -> Union[dict, None]:
60
+ """
61
+ Function to get the default network interface.
62
+ :return: dict[interface_name: details] or None.
63
+ """
64
+ # Get all interfaces.
65
+ interfaces: dict = psutil.net_if_addrs()
66
+ default_ip_address: str = networks.get_default_internet_ipv4()
67
+
68
+ for interface, details in interfaces.items():
69
+ for address in details:
70
+ # Check if the address is an IPv4 address (AF_INET) and not a loopback (127.0.0.1)
71
+ if address.family == socket.AF_INET and not address.address.startswith('127.'):
72
+ # Check if the address is the default IP address.
73
+ if address.address == default_ip_address:
74
+ return {interface: details}
75
+
76
+ return None
77
+
78
+
79
+ def list_network_interfaces() -> list:
80
+ """
81
+ Function to list all network interfaces.
82
+ :return: list of interface names.
83
+ """
84
+ iface_names = list(psutil.net_if_addrs().keys())
85
+ return iface_names
@@ -1,4 +1,4 @@
1
- import random
1
+ from secrets import randbits
2
2
  from OpenSSL import crypto
3
3
 
4
4
  from .. import certificates
@@ -121,7 +121,14 @@ def generate_server_certificate_empty(
121
121
  """
122
122
 
123
123
  cert = crypto.X509()
124
- cert.set_serial_number(random.randint(0, 2 ** 64 - 1))
124
+
125
+ # RFC 5280: serial must be positive and non-zero
126
+ # 1 … 2⁶⁴-1
127
+ cert.set_serial_number(randbits(64))
128
+ current_serial = cert.get_serial_number()
129
+ if current_serial <= 0:
130
+ raise RuntimeError(f"Refusing to create a certificate with non-positive serial: {str(current_serial)}")
131
+
125
132
  cert.get_subject().CN = certname
126
133
 
127
134
  cert.set_version(2)
@@ -0,0 +1,116 @@
1
+ from typing import Literal
2
+ import win32crypt as wcrypt
3
+
4
+ from ...print_api import print_api
5
+ from ... import certificates
6
+
7
+
8
+ # lpszStoreProvider
9
+ CERT_STORE_PROV_SYSTEM = 0x0000000A
10
+ # dwFlags
11
+ CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
12
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
13
+ CERT_CLOSE_STORE_FORCE_FLAG = 0x00000001
14
+ CRYPT_STRING_BASE64HEADER = 0x00000000
15
+ X509_ASN_ENCODING = 0x00000001
16
+ CERT_STORE_ADD_REPLACE_EXISTING = 3
17
+
18
+
19
+ STORE_LOCATION_TO_CERT_SYSTEM_STORE: dict = {
20
+ "ROOT": CERT_SYSTEM_STORE_LOCAL_MACHINE,
21
+ "CA": CERT_SYSTEM_STORE_LOCAL_MACHINE,
22
+ "MY": CERT_SYSTEM_STORE_CURRENT_USER
23
+ }
24
+
25
+
26
+ def delete_certificate_by_issuer_name(
27
+ issuer_name: str,
28
+ store_location: Literal[
29
+ "ROOT",
30
+ "CA",
31
+ "MY"] = "ROOT",
32
+ print_kwargs: dict = None
33
+ ):
34
+ """
35
+ NEED ADMIN RIGHTS.
36
+ The function will remove all certificates with the specified issuer name.
37
+ There can be several certificates with this name.
38
+
39
+ :param issuer_name: string, issuer name to search for.
40
+ :param store_location: string, store location to search in. Default is "ROOT".
41
+ :param print_kwargs: dict, print_api kwargs.
42
+ """
43
+
44
+ """
45
+ WinAPI doesn't like to do 2 actions in one iteration. So, first we will collect all certificates to remove,
46
+ and in the second iteration remove them.
47
+
48
+ Full Explanation:
49
+ When you iterate with for cert in store.CertEnumCertificatesInStore(): and call
50
+ cert.CertDeleteCertificateFromStore() inside that loop, you’re modifying the underlying certificate store
51
+ while its internal enumeration is still active. This can lead to a segmentation fault (access violation 0xC0000005).
52
+ By collecting the certificates in the first pass, you freeze the iteration so the store
53
+ doesn’t get mutated mid-enumeration.
54
+ In the second pass, when you actually remove them, you’re no longer in the middle of enumerating.
55
+ This prevents the store’s pointer from becoming invalid.
56
+
57
+ This approach should stop the Process finished with exit code -1073741819 (0xC0000005) issue.
58
+ """
59
+
60
+ store = wcrypt.CertOpenStore(
61
+ CERT_STORE_PROV_SYSTEM,
62
+ 0,
63
+ None,
64
+ STORE_LOCATION_TO_CERT_SYSTEM_STORE[store_location],
65
+ store_location
66
+ )
67
+
68
+ # Collect all matching certificates in a list
69
+ certs_to_remove = []
70
+ for cert in store.CertEnumCertificatesInStore():
71
+ # Certificate properties.
72
+ # cert.CertEnumCertificateContextProperties()
73
+ subject_string: str = wcrypt.CertNameToStr(cert.Subject)
74
+ if subject_string == issuer_name:
75
+ # Remove the certificate.
76
+ certs_to_remove.append(cert)
77
+
78
+ # Remove all certificates from the list.
79
+ for cert in certs_to_remove:
80
+ cert.CertDeleteCertificateFromStore()
81
+ print_api(f"Removed the Certificate from store [{store_location}] with issuer [{issuer_name}]", **(print_kwargs or {}))
82
+
83
+ # There is an exception about store close.
84
+ # store.CertCloseStore()
85
+
86
+
87
+ def install_certificate_file(
88
+ file_path: str,
89
+ store_location: Literal[
90
+ "ROOT", "CA", "MY"] = "ROOT",
91
+ print_kwargs: dict = None
92
+ ):
93
+ """
94
+ NEED ADMIN RIGHTS.
95
+ The function will install the certificate from the file to the specified store location.
96
+
97
+ :param file_path: string, full file path to the certificate file.
98
+ :param store_location: string, store location to install the certificate. Default is "ROOT".
99
+ :param print_kwargs: dict, print_api kwargs.
100
+ """
101
+
102
+ with open(file_path, 'r') as f:
103
+ certificate_string = f.read()
104
+
105
+ certificate_pem = certificates.get_pem_certificate_from_string(certificate_string)
106
+
107
+ certificate_bytes = wcrypt.CryptStringToBinary(certificate_pem, CRYPT_STRING_BASE64HEADER)[0]
108
+
109
+ store = wcrypt.CertOpenStore(
110
+ CERT_STORE_PROV_SYSTEM, 0, None, STORE_LOCATION_TO_CERT_SYSTEM_STORE[store_location], store_location)
111
+
112
+ store.CertAddEncodedCertificateToStore(X509_ASN_ENCODING, certificate_bytes, CERT_STORE_ADD_REPLACE_EXISTING)
113
+ store.CertCloseStore(CERT_CLOSE_STORE_FORCE_FLAG)
114
+
115
+ message = f"Certificate installed to the store: [{store_location}] from file: [{file_path}]"
116
+ print_api(message, **(print_kwargs or {}))
@@ -0,0 +1,174 @@
1
+ import win32evtlog
2
+ import win32security
3
+ import win32con
4
+
5
+
6
+ def get_latest_events(
7
+ server_ip: str = ".",
8
+ username: str = None,
9
+ password: str = None,
10
+ domain: str = ".",
11
+ log_name: str = "Security",
12
+ count: int = None,
13
+ event_id_list: list[int] | None = None
14
+ ):
15
+ """
16
+ Fetch latest `count` events from a Windows Event Log (local or remote) using pywin32.
17
+
18
+ - If username/password are None => use current security context.
19
+ - If server_ip is ".", "localhost", "127.0.0.1", "" or None => open local log.
20
+ - If count is None => return *all* events in the log.
21
+ - If event_id_list is not None => only return events whose EventID is in that list.
22
+
23
+ :param server_ip: IPv4/hostname of remote machine, or "." / None for local
24
+ :param username: Username to authenticate with (optional)
25
+ :param password: Password for the user (optional)
26
+ :param domain: Domain or computer name; ignored if username is None.
27
+ If None and username is given, "." is used.
28
+ :param log_name: Log name (e.g. "Security", "System", "Application")
29
+ :param count: Number of most recent events to return, or None for all
30
+ :param event_id_list: List of Event IDs (low 16-bit) to include, or None for all
31
+ :return: List of dicts describing events (most recent first)
32
+ """
33
+
34
+ # Normalize server for OpenEventLog: None means "local machine"
35
+ normalized_server = server_ip
36
+ if server_ip in (None, "", ".", "localhost", "127.0.0.1"):
37
+ normalized_server = None
38
+
39
+ # Max events logic: None => infinite
40
+ max_events = float("inf") if count is None else count
41
+
42
+ # Precompute set of event IDs for fast membership checks
43
+ event_ids_set = set(event_id_list) if event_id_list is not None else None
44
+
45
+ # Decide whether we need impersonation
46
+ use_impersonation = username is not None and password is not None
47
+
48
+ h_user = None
49
+ events = []
50
+
51
+ try:
52
+ if use_impersonation:
53
+ if domain is None:
54
+ domain = "."
55
+
56
+ # Log on with explicit credentials and impersonate for the remote call
57
+ # LOGON32_LOGON_NEW_CREDENTIALS lets us use these creds for remote access
58
+ # while keeping the local token mostly unchanged.
59
+ h_user = win32security.LogonUser(
60
+ username,
61
+ domain,
62
+ password,
63
+ win32con.LOGON32_LOGON_NEW_CREDENTIALS,
64
+ win32con.LOGON32_PROVIDER_WINNT50,
65
+ )
66
+
67
+ win32security.ImpersonateLoggedOnUser(h_user)
68
+
69
+ # Connect to remote event log
70
+ # `server_ip` can be an IP or hostname; no need for leading "\\".
71
+ # local if normalized_server is None.
72
+ h_log = win32evtlog.OpenEventLog(normalized_server, log_name)
73
+
74
+ flags = (
75
+ win32evtlog.EVENTLOG_BACKWARDS_READ
76
+ | win32evtlog.EVENTLOG_SEQUENTIAL_READ
77
+ )
78
+
79
+ offset = 0 # not used with BACKWARDS_READ + SEQUENTIAL_READ, but kept for clarity
80
+
81
+ while len(events) < max_events:
82
+ records = win32evtlog.ReadEventLog(h_log, flags, offset)
83
+ if not records:
84
+ break
85
+
86
+ for ev in records:
87
+ # Low 16 bits are the actual Event ID
88
+ eid = ev.EventID & 0xFFFF
89
+
90
+ # If filtering by event IDs, skip others
91
+ if event_ids_set is not None and eid not in event_ids_set:
92
+ continue
93
+
94
+ raw_strings = list(ev.StringInserts or [])
95
+ strings_dict = _parse_strings(eid, ev.SourceName, raw_strings)
96
+
97
+ evt = {
98
+ "RecordNumber": ev.RecordNumber,
99
+ "TimeGenerated": ev.TimeGenerated.Format(), # string time
100
+ "ComputerName": ev.ComputerName,
101
+ "SourceName": ev.SourceName,
102
+ # Low 16 bits are the actual Event ID
103
+ "EventID": ev.EventID & 0xFFFF,
104
+ "EventType": ev.EventType,
105
+ "EventCategory": ev.EventCategory,
106
+ "Strings": raw_strings,
107
+ "StringsDict": strings_dict,
108
+ }
109
+ events.append(evt)
110
+
111
+ if len(events) >= max_events:
112
+ break
113
+
114
+ win32evtlog.CloseEventLog(h_log)
115
+
116
+ finally:
117
+ # Clean up impersonation if we used it, Always revert impersonation and close handle
118
+ if use_impersonation and h_user is not None:
119
+ win32security.RevertToSelf()
120
+ h_user.Close()
121
+
122
+ # `events` is in newest to oldest already because of BACKWARDS_READ.
123
+ return events
124
+
125
+
126
+ def _parse_strings(event_id: int, source_name: str, strings: list[str]) -> dict:
127
+ """
128
+ Convert the raw 'Strings' list into a dictionary with friendly field names
129
+ for specific event IDs. For unknown events, fall back to String1, String2, ...
130
+
131
+ Currently has a special mapping for:
132
+ - 5156 (Security log, WFP allowed connection)
133
+ """
134
+ if not strings:
135
+ return {}
136
+
137
+ # Normalize source name a bit
138
+ src = (source_name or "").lower()
139
+
140
+ # Special-case: Security 5156 (Windows Filtering Platform has permitted a connection)
141
+ # Insertion strings (in order) are:
142
+ # 1 Process ID
143
+ # 2 Application Name
144
+ # 3 Direction
145
+ # 4 Source Address
146
+ # 5 Source Port
147
+ # 6 Destination Address
148
+ # 7 Destination Port
149
+ # 8 Protocol
150
+ # 9 Filter Run-Time ID
151
+ # 10 Layer Name
152
+ # 11 Layer Run-Time ID
153
+ if event_id == 5156 and "security-auditing" in src:
154
+ keys_5156 = [
155
+ "Process ID",
156
+ "Application Name",
157
+ "Direction",
158
+ "Source Address",
159
+ "Source Port",
160
+ "Destination Address",
161
+ "Destination Port",
162
+ "Protocol",
163
+ "Filter Run-Time ID",
164
+ "Layer Name",
165
+ "Layer Run-Time ID",
166
+ ]
167
+ return {
168
+ key: strings[i]
169
+ for i, key in enumerate(keys_5156)
170
+ if i < len(strings)
171
+ }
172
+
173
+ # Default: generic mapping
174
+ return {f"String{i+1}": s for i, s in enumerate(strings)}
@@ -1,12 +1,7 @@
1
- import subprocess
2
- import winreg
3
-
4
1
  from .. import subscribe
5
- from .....print_api import print_api
2
+ from .... import win_auditw
6
3
 
7
4
 
8
- AUDITING_REG_PATH: str = r"Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit"
9
- PROCESS_CREATION_INCLUDE_CMDLINE_VALUE: str = "ProcessCreationIncludeCmdLine_Enabled"
10
5
  LOG_CHANNEL: str = 'Security'
11
6
  EVENT_ID: int = 4688
12
7
 
@@ -30,8 +25,8 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
30
25
 
31
26
  def start(self):
32
27
  """Start the subscription process."""
33
- enable_audit_process_creation()
34
- enable_command_line_auditing()
28
+ win_auditw.enable_audit_process_creation()
29
+ win_auditw.enable_command_line_auditing()
35
30
 
36
31
  super().start()
37
32
 
@@ -60,100 +55,3 @@ class ProcessCreateSubscriber(subscribe.EventLogSubscriber):
60
55
  # print(f"Error looking up account SID: {e}")
61
56
 
62
57
  return data
63
-
64
-
65
- def enable_audit_process_creation(print_kwargs: dict = None):
66
- """
67
- Enable the 'Audit Process Creation' policy.
68
-
69
- :param print_kwargs: Optional keyword arguments for the print function.
70
- """
71
- if is_audit_process_creation_enabled():
72
- print_api("Audit Process Creation is already enabled.", color='yellow', **(print_kwargs or {}))
73
- return
74
-
75
- # Enable "Audit Process Creation" policy
76
- audit_policy_command = [
77
- "auditpol", "/set", "/subcategory:Process Creation", "/success:enable", "/failure:enable"
78
- ]
79
- try:
80
- subprocess.run(audit_policy_command, check=True)
81
- print_api("Successfully enabled 'Audit Process Creation'.", color='green', **(print_kwargs or {}))
82
- except subprocess.CalledProcessError as e:
83
- print_api(f"Failed to enable 'Audit Process Creation': {e}", error_type=True, color='red', **(print_kwargs or {}))
84
- raise e
85
-
86
-
87
- def is_audit_process_creation_enabled(print_kwargs: dict = None) -> bool:
88
- """
89
- Check if the 'Audit Process Creation' policy is enabled.
90
-
91
- :param print_kwargs: Optional keyword arguments for the print function.
92
- """
93
- # Command to check the "Audit Process Creation" policy
94
- audit_policy_check_command = [
95
- "auditpol", "/get", "/subcategory:Process Creation"
96
- ]
97
- try:
98
- result = subprocess.run(audit_policy_check_command, check=True, capture_output=True, text=True)
99
- output = result.stdout
100
- # print_api(output) # Print the output for inspection
101
-
102
- if "Process Creation" in output and "Success and Failure" in output:
103
- # print_api(
104
- # "'Audit Process Creation' is enabled for both success and failure.",
105
- # color='green', **(print_kwargs or {}))
106
- return True
107
- else:
108
- # print_api(output, **(print_kwargs or {}))
109
- # print_api(
110
- # "'Audit Process Creation' is not fully enabled. Check the output above for details.",
111
- # color='yellow', **(print_kwargs or {}))
112
- return False
113
- except subprocess.CalledProcessError as e:
114
- print_api(f"Failed to check 'Audit Process Creation': {e}", color='red', error_type=True, **(print_kwargs or {}))
115
- return False
116
-
117
-
118
- def enable_command_line_auditing(print_kwargs: dict = None):
119
- """
120
- Enable the 'Include command line in process creation events' policy.
121
-
122
- :param print_kwargs: Optional keyword arguments for the print function.
123
- """
124
-
125
- if is_command_line_auditing_enabled():
126
- print_api(
127
- "'Include command line in process creation events' is already enabled.", color='yellow',
128
- **(print_kwargs or {}))
129
- return
130
-
131
- try:
132
- # Open the registry key
133
- with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH) as reg_key:
134
- # Set the value
135
- winreg.SetValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE, 0, winreg.REG_DWORD, 1)
136
-
137
- print_api(
138
- "Successfully enabled 'Include command line in process creation events'.",
139
- color='green', **(print_kwargs or {}))
140
- except WindowsError as e:
141
- print_api(
142
- f"Failed to enable 'Include command line in process creation events': {e}", error_type=True,
143
- color='red', **(print_kwargs or {}))
144
-
145
-
146
- def is_command_line_auditing_enabled():
147
- try:
148
- # Open the registry key
149
- with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, AUDITING_REG_PATH, 0, winreg.KEY_READ) as reg_key:
150
- # Query the value
151
- value, reg_type = winreg.QueryValueEx(reg_key, PROCESS_CREATION_INCLUDE_CMDLINE_VALUE)
152
- # Check if the value is 1 (enabled)
153
- return value == 1
154
- except FileNotFoundError:
155
- # Key or value not found, assume it's not enabled
156
- return False
157
- except WindowsError as e:
158
- print(f"Failed to read the 'Include command line in process creation events' setting: {e}")
159
- return False