atomicshop 2.11.47__py3-none-any.whl → 3.10.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/addons/process_list/compile.cmd +7 -0
  4. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  5. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  6. atomicshop/a_mains/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  7. atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +8 -1
  8. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  9. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  10. atomicshop/a_mains/github_wrapper.py +11 -0
  11. atomicshop/a_mains/install_ca_certificate.py +172 -0
  12. atomicshop/{addons/mains → a_mains}/msi_unpacker.py +3 -1
  13. atomicshop/a_mains/process_from_port.py +119 -0
  14. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  15. atomicshop/a_mains/update_config_toml.py +38 -0
  16. atomicshop/appointment_management.py +5 -3
  17. atomicshop/basics/ansi_escape_codes.py +3 -1
  18. atomicshop/basics/argparse_template.py +2 -0
  19. atomicshop/basics/booleans.py +27 -30
  20. atomicshop/basics/bytes_arrays.py +43 -0
  21. atomicshop/basics/classes.py +149 -1
  22. atomicshop/basics/dicts.py +12 -0
  23. atomicshop/basics/enums.py +2 -2
  24. atomicshop/basics/exceptions.py +5 -1
  25. atomicshop/basics/list_of_classes.py +29 -0
  26. atomicshop/basics/list_of_dicts.py +69 -5
  27. atomicshop/basics/lists.py +14 -0
  28. atomicshop/basics/multiprocesses.py +374 -50
  29. atomicshop/basics/package_module.py +10 -0
  30. atomicshop/basics/strings.py +160 -7
  31. atomicshop/basics/threads.py +14 -0
  32. atomicshop/basics/tracebacks.py +13 -4
  33. atomicshop/certificates.py +153 -52
  34. atomicshop/config_init.py +12 -7
  35. atomicshop/console_user_response.py +7 -14
  36. atomicshop/consoles.py +9 -0
  37. atomicshop/datetimes.py +98 -0
  38. atomicshop/diff_check.py +340 -40
  39. atomicshop/dns.py +128 -12
  40. atomicshop/etws/_pywintrace_fix.py +17 -0
  41. atomicshop/etws/const.py +38 -0
  42. atomicshop/etws/providers.py +21 -0
  43. atomicshop/etws/sessions.py +43 -0
  44. atomicshop/etws/trace.py +168 -0
  45. atomicshop/etws/traces/trace_dns.py +162 -0
  46. atomicshop/etws/traces/trace_sysmon_process_creation.py +126 -0
  47. atomicshop/etws/traces/trace_tcp.py +130 -0
  48. atomicshop/file_io/csvs.py +222 -24
  49. atomicshop/file_io/docxs.py +35 -18
  50. atomicshop/file_io/file_io.py +35 -19
  51. atomicshop/file_io/jsons.py +49 -0
  52. atomicshop/file_io/tomls.py +139 -0
  53. atomicshop/filesystem.py +864 -293
  54. atomicshop/get_process_list.py +133 -0
  55. atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +52 -19
  56. atomicshop/http_parse.py +149 -93
  57. atomicshop/ip_addresses.py +6 -1
  58. atomicshop/mitm/centered_settings.py +132 -0
  59. atomicshop/mitm/config_static.py +207 -0
  60. atomicshop/mitm/config_toml_editor.py +55 -0
  61. atomicshop/mitm/connection_thread_worker.py +875 -357
  62. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  63. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  64. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  65. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  66. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  67. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  68. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  69. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  70. atomicshop/mitm/engines/create_module_template.py +58 -14
  71. atomicshop/mitm/import_config.py +359 -139
  72. atomicshop/mitm/initialize_engines.py +160 -74
  73. atomicshop/mitm/message.py +64 -23
  74. atomicshop/mitm/mitm_main.py +892 -0
  75. atomicshop/mitm/recs_files.py +183 -0
  76. atomicshop/mitm/shared_functions.py +4 -10
  77. atomicshop/mitm/ssh_tester.py +82 -0
  78. atomicshop/mitm/statistic_analyzer.py +257 -166
  79. atomicshop/mitm/statistic_analyzer_helper/analyzer_helper.py +136 -0
  80. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +525 -0
  81. atomicshop/monitor/change_monitor.py +96 -120
  82. atomicshop/monitor/checks/dns.py +139 -70
  83. atomicshop/monitor/checks/file.py +77 -0
  84. atomicshop/monitor/checks/network.py +81 -77
  85. atomicshop/monitor/checks/process_running.py +33 -34
  86. atomicshop/monitor/checks/url.py +94 -0
  87. atomicshop/networks.py +671 -0
  88. atomicshop/on_exit.py +205 -0
  89. atomicshop/package_mains_processor.py +84 -0
  90. atomicshop/permissions/permissions.py +22 -0
  91. atomicshop/permissions/ubuntu_permissions.py +239 -0
  92. atomicshop/permissions/win_permissions.py +33 -0
  93. atomicshop/print_api.py +24 -41
  94. atomicshop/process.py +63 -17
  95. atomicshop/process_poller/__init__.py +0 -0
  96. atomicshop/process_poller/pollers/__init__.py +0 -0
  97. atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
  98. atomicshop/process_poller/process_pool.py +207 -0
  99. atomicshop/process_poller/simple_process_pool.py +311 -0
  100. atomicshop/process_poller/tracer_base.py +45 -0
  101. atomicshop/process_poller/tracers/__init__.py +0 -0
  102. atomicshop/process_poller/tracers/event_log.py +46 -0
  103. atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
  104. atomicshop/python_file_patcher.py +1 -1
  105. atomicshop/python_functions.py +27 -75
  106. atomicshop/question_answer_engine.py +2 -2
  107. atomicshop/scheduling.py +24 -5
  108. atomicshop/sound.py +4 -2
  109. atomicshop/speech_recognize.py +8 -0
  110. atomicshop/ssh_remote.py +158 -172
  111. atomicshop/startup/__init__.py +0 -0
  112. atomicshop/startup/win/__init__.py +0 -0
  113. atomicshop/startup/win/startup_folder.py +53 -0
  114. atomicshop/startup/win/task_scheduler.py +119 -0
  115. atomicshop/system_resource_monitor.py +61 -46
  116. atomicshop/system_resources.py +8 -8
  117. atomicshop/tempfiles.py +1 -2
  118. atomicshop/timer.py +30 -11
  119. atomicshop/urls.py +41 -0
  120. atomicshop/venvs.py +28 -0
  121. atomicshop/versioning.py +27 -0
  122. atomicshop/web.py +110 -25
  123. atomicshop/web_apis/__init__.py +0 -0
  124. atomicshop/web_apis/google_custom_search.py +44 -0
  125. atomicshop/web_apis/google_llm.py +188 -0
  126. atomicshop/websocket_parse.py +450 -0
  127. atomicshop/wrappers/certauthw/certauth.py +1 -0
  128. atomicshop/wrappers/cryptographyw.py +29 -8
  129. atomicshop/wrappers/ctyping/etw_winapi/__init__.py +0 -0
  130. atomicshop/wrappers/ctyping/etw_winapi/const.py +335 -0
  131. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +393 -0
  132. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  133. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  134. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +13 -9
  135. atomicshop/wrappers/ctyping/msi_windows_installer/tables.py +35 -0
  136. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  137. atomicshop/wrappers/ctyping/win_console.py +39 -0
  138. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  139. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  140. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  141. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  142. atomicshop/wrappers/factw/get_file_data.py +12 -5
  143. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  144. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  145. atomicshop/wrappers/factw/postgresql/firmware.py +4 -6
  146. atomicshop/wrappers/githubw.py +583 -51
  147. atomicshop/wrappers/loggingw/consts.py +49 -0
  148. atomicshop/wrappers/loggingw/filters.py +102 -0
  149. atomicshop/wrappers/loggingw/formatters.py +58 -71
  150. atomicshop/wrappers/loggingw/handlers.py +459 -40
  151. atomicshop/wrappers/loggingw/loggers.py +19 -0
  152. atomicshop/wrappers/loggingw/loggingw.py +1010 -178
  153. atomicshop/wrappers/loggingw/reading.py +344 -19
  154. atomicshop/wrappers/mongodbw/__init__.py +0 -0
  155. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  156. atomicshop/wrappers/mongodbw/mongodbw.py +1432 -0
  157. atomicshop/wrappers/netshw.py +271 -0
  158. atomicshop/wrappers/playwrightw/engine.py +34 -19
  159. atomicshop/wrappers/playwrightw/infra.py +5 -0
  160. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  161. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  162. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  163. atomicshop/wrappers/playwrightw/waits.py +9 -7
  164. atomicshop/wrappers/powershell_networking.py +80 -0
  165. atomicshop/wrappers/psutilw/processes.py +81 -0
  166. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  167. atomicshop/wrappers/psutilw/psutilw.py +9 -0
  168. atomicshop/wrappers/pyopensslw.py +9 -2
  169. atomicshop/wrappers/pywin32w/__init__.py +0 -0
  170. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  171. atomicshop/wrappers/pywin32w/console.py +34 -0
  172. atomicshop/wrappers/pywin32w/win_event_log/__init__.py +0 -0
  173. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  174. atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +212 -0
  175. atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py +0 -0
  176. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +57 -0
  177. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +49 -0
  178. atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py +97 -0
  179. atomicshop/wrappers/pywin32w/winshell.py +19 -0
  180. atomicshop/wrappers/pywin32w/wmis/__init__.py +0 -0
  181. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  182. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  183. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  184. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  185. atomicshop/wrappers/socketw/accepter.py +21 -7
  186. atomicshop/wrappers/socketw/certificator.py +216 -150
  187. atomicshop/wrappers/socketw/creator.py +190 -50
  188. atomicshop/wrappers/socketw/dns_server.py +500 -173
  189. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  190. atomicshop/wrappers/socketw/process_getter.py +86 -0
  191. atomicshop/wrappers/socketw/receiver.py +144 -102
  192. atomicshop/wrappers/socketw/sender.py +65 -35
  193. atomicshop/wrappers/socketw/sni.py +334 -165
  194. atomicshop/wrappers/socketw/socket_base.py +134 -0
  195. atomicshop/wrappers/socketw/socket_client.py +137 -95
  196. atomicshop/wrappers/socketw/socket_server_tester.py +14 -9
  197. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  198. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  199. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  200. atomicshop/wrappers/sysmonw.py +157 -0
  201. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  202. atomicshop/wrappers/win_auditw.py +189 -0
  203. atomicshop/wrappers/winregw/__init__.py +0 -0
  204. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  205. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  206. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -49
  207. atomicshop-3.10.5.dist-info/RECORD +306 -0
  208. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  209. atomicshop/_basics_temp.py +0 -101
  210. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  211. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  212. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  213. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  214. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  215. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  216. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  217. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  218. atomicshop/addons/package_setup/Setup.cmd +0 -7
  219. atomicshop/addons/process_list/compile.cmd +0 -2
  220. atomicshop/addons/process_list/compiled/Win10x64/process_list.dll +0 -0
  221. atomicshop/addons/process_list/compiled/Win10x64/process_list.exp +0 -0
  222. atomicshop/addons/process_list/compiled/Win10x64/process_list.lib +0 -0
  223. atomicshop/archiver/_search_in_zip.py +0 -189
  224. atomicshop/archiver/archiver.py +0 -34
  225. atomicshop/archiver/search_in_archive.py +0 -250
  226. atomicshop/archiver/sevenz_app_w.py +0 -86
  227. atomicshop/archiver/sevenzs.py +0 -44
  228. atomicshop/archiver/zips.py +0 -293
  229. atomicshop/etw/dns_trace.py +0 -118
  230. atomicshop/etw/etw.py +0 -61
  231. atomicshop/file_types.py +0 -24
  232. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  233. atomicshop/mitm/initialize_mitm_server.py +0 -240
  234. atomicshop/monitor/checks/hash.py +0 -44
  235. atomicshop/monitor/checks/hash_checks/file.py +0 -55
  236. atomicshop/monitor/checks/hash_checks/url.py +0 -62
  237. atomicshop/pbtkmultifile_argparse.py +0 -88
  238. atomicshop/permissions.py +0 -110
  239. atomicshop/process_poller.py +0 -237
  240. atomicshop/script_as_string_processor.py +0 -38
  241. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  242. atomicshop/ssh_scripts/process_from_port.py +0 -27
  243. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  244. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  245. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  246. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  247. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  248. atomicshop/wrappers/ffmpegw.py +0 -125
  249. atomicshop/wrappers/loggingw/checks.py +0 -20
  250. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  251. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  252. atomicshop/wrappers/socketw/base.py +0 -59
  253. atomicshop/wrappers/socketw/get_process.py +0 -107
  254. atomicshop/wrappers/wslw.py +0 -191
  255. atomicshop-2.11.47.dist-info/RECORD +0 -251
  256. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  257. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  258. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  259. /atomicshop/{addons/mains → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  260. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  261. /atomicshop/{addons/mains → a_mains}/search_for_hyperlinks_in_docx.py +0 -0
  262. /atomicshop/{archiver → etws}/__init__.py +0 -0
  263. /atomicshop/{etw → etws/traces}/__init__.py +0 -0
  264. /atomicshop/{monitor/checks/hash_checks → mitm/statistic_analyzer_helper}/__init__.py +0 -0
  265. /atomicshop/{wrappers/nodejsw → permissions}/__init__.py +0 -0
  266. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  267. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  268. {atomicshop-2.11.47.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,393 @@
1
+ import ctypes
2
+ import queue
3
+ from ctypes.wintypes import ULONG
4
+ import uuid
5
+ from typing import Literal
6
+
7
+ from . import const
8
+ from ....etws import providers
9
+
10
+ class ETWSessionExists(Exception):
11
+ pass
12
+
13
+
14
+ # Convert the GUID string to a GUID structure
15
+ def _string_to_guid(guid_string):
16
+ guid_string = guid_string.strip('{}') # Remove curly braces
17
+ parts = guid_string.split('-')
18
+ return const.GUID(
19
+ Data1=int(parts[0], 16),
20
+ Data2=int(parts[1], 16),
21
+ Data3=int(parts[2], 16),
22
+ Data4=(ctypes.c_ubyte * 8)(*[
23
+ int(parts[3][i:i+2], 16) for i in range(0, 4, 2)
24
+ ] + [
25
+ int(parts[4][i:i+2], 16) for i in range(0, 12, 2)
26
+ ])
27
+ )
28
+
29
+
30
+ # Set up the ETW session
31
+ def start_etw_session(
32
+ session_name: str,
33
+ provider_guid_list: list = None,
34
+ provider_name_list: list = None,
35
+ verbosity_mode: int = 4,
36
+ maximum_buffers: int = 38
37
+ ) -> const.TRACEHANDLE:
38
+ """
39
+ Start an ETW session and enable the specified provider.
40
+
41
+ :param session_name: The name of the session to start.
42
+ :param provider_guid_list: The GUID list of the providers to enable.
43
+ :param provider_name_list: The name list of the providers to enable.
44
+ :param verbosity_mode: The verbosity level of the events to capture.
45
+ 0 - Always: Capture all events. This is typically used for critical events that should always be logged.
46
+ 1 - Critical: Capture critical events that indicate a severe problem.
47
+ 2 - Error: Capture error events that indicate a problem but are not critical.
48
+ 3 - Warning: Capture warning events that indicate a potential problem.
49
+ 4 - Information: Capture informational events that are not indicative of problems.
50
+ 5 - Verbose: Capture detailed trace events for diagnostic purposes.
51
+ :param maximum_buffers: The maximum number of buffers to use.
52
+ 0 or 16: The default value of ETW class. If you put 0, it will be converted to 16 by default by ETW itself.
53
+ 38: The maximum number of buffers that can be used.
54
+
55
+ Event Handling Capacity:
56
+ 16 Buffers: With fewer buffers, the session can handle a smaller volume of events before needing to flush
57
+ the buffers to the log file or before a real-time consumer needs to process them. If the buffers fill up
58
+ quickly and cannot be processed in time, events might be lost.
59
+ 38 Buffers: With more buffers, the session can handle a higher volume of events. This reduces the
60
+ likelihood of losing events in high-traffic scenarios because more events can be held in memory before
61
+ they need to be processed or written to a log file.
62
+ Performance Considerations:
63
+ 16 Buffers: Requires less memory, but may be prone to event loss under heavy load if the buffers fill up
64
+ faster than they can be processed.
65
+ 38 Buffers: Requires more memory, but can improve reliability in capturing all events under heavy load by
66
+ providing more buffer space. However, it can also increase the memory footprint of the application or
67
+ system running the ETW session.
68
+ """
69
+
70
+ if not provider_guid_list and not provider_name_list:
71
+ raise ValueError("Either 'provider_guid_list' or 'provider_name_list' must be provided")
72
+ elif provider_guid_list and provider_name_list:
73
+ raise ValueError("Only one of 'provider_guid_list' or 'provider_name_list' must be provided")
74
+
75
+ if provider_name_list:
76
+ provider_guid_list = []
77
+ for provider_name in provider_name_list:
78
+ provider_guid_list.append(providers.get_provider_guid_by_name(provider_name))
79
+
80
+ # (1) Allocate a buffer large enough for EVENT_TRACE_PROPERTIES + space for 2 strings
81
+ # The typical approach is to allow enough space for:
82
+ # - The structure
83
+ # - The "LoggerName" string
84
+ # - The "LogFileName" string (even if we don't use it, we must leave offset space)
85
+ #
86
+ props_size = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
87
+ # Add space for 2 x 1024 wide-chars (one for LoggerName, one for LogFileName)
88
+ # Each wide char = ctypes.sizeof(ctypes.c_wchar).
89
+ props_size += (2 * 1024 * ctypes.sizeof(ctypes.c_wchar)) # for logger name
90
+ props_size += (2 * 1024 * ctypes.sizeof(ctypes.c_wchar)) # for log file name
91
+
92
+ properties_buffer = ctypes.create_string_buffer(props_size)
93
+ properties_ptr = ctypes.cast(properties_buffer, ctypes.POINTER(const.EVENT_TRACE_PROPERTIES))
94
+ props = properties_ptr.contents
95
+
96
+ # (2) Fill in basic fields
97
+ props.Wnode.BufferSize = props_size
98
+ props.Wnode.Flags = const.WNODE_FLAG_TRACED_GUID # 0x00020000
99
+ props.Wnode.ClientContext = 1 # QPC clock
100
+ props.BufferSize = 1024
101
+ props.MinimumBuffers = 1
102
+ props.MaximumBuffers = maximum_buffers
103
+ props.MaximumFileSize = 0
104
+ props.LogFileMode = const.EVENT_TRACE_REAL_TIME_MODE # real-time
105
+ props.FlushTimer = 1
106
+ props.EnableFlags = 0
107
+
108
+ # (3) Indicate where in this allocated buffer the strings should go
109
+ struct_size = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
110
+ props.LoggerNameOffset = struct_size
111
+ props.LogFileNameOffset = struct_size + (2 * 1024 * ctypes.sizeof(ctypes.c_wchar))
112
+
113
+ # (4) Copy the session name into the LoggerName space
114
+ logger_name_address = ctypes.addressof(properties_buffer) + props.LoggerNameOffset
115
+ session_name_wchar = ctypes.create_unicode_buffer(session_name)
116
+ ctypes.memmove(
117
+ logger_name_address,
118
+ session_name_wchar,
119
+ len(session_name) * ctypes.sizeof(ctypes.c_wchar)
120
+ )
121
+
122
+ # (5) Start the session
123
+ session_handle = const.TRACEHANDLE(0)
124
+ status = const.StartTrace(
125
+ ctypes.byref(session_handle),
126
+ session_name, # The session name as an LPCWSTR
127
+ properties_ptr # pointer to EVENT_TRACE_PROPERTIES
128
+ )
129
+
130
+ if status != 0:
131
+ # 183 => ERROR_ALREADY_EXISTS
132
+ if status == 183:
133
+ raise ETWSessionExists(f"ETW session [{session_name}] already exists")
134
+ else:
135
+ raise Exception(f"StartTraceW failed with error code {status}")
136
+
137
+ # (6) If we have providers to enable, enable each
138
+ if provider_guid_list:
139
+ for guid_str in provider_guid_list:
140
+ guid_struct = _string_to_guid(guid_str)
141
+
142
+ # Typically you'd do something like:
143
+ # advapi32.EnableTraceEx2(TRACEHANDLE, PGUID, CONTROL_CODE, LEVEL, KW, KW, TIMEOUT, FILTER)
144
+
145
+ enable_status = const.EnableTraceEx2(
146
+ session_handle, # The session handle
147
+ ctypes.byref(guid_struct), # The provider GUID
148
+ const.EVENT_CONTROL_CODE_ENABLE_PROVIDER, # 1 => enable
149
+ verbosity_mode, # level
150
+ 0xFFFFFFFFFFFFFFFF, # matchAnyKeyword
151
+ 0, # matchAllKeyword
152
+ 0, # timeout
153
+ None # enableParameters (optional)
154
+ )
155
+
156
+ if enable_status != 0:
157
+ raise Exception(
158
+ f"EnableTraceEx2 failed for provider {guid_str} (error={enable_status})"
159
+ )
160
+
161
+ print(f"ETW session '{session_name}' started successfully.")
162
+ return session_handle
163
+
164
+
165
+ # Function to stop and delete ETW session
166
+ def stop_and_delete_etw_session(session_name: str) -> tuple[bool, int]:
167
+ """
168
+ Stop and delete ETW session.
169
+
170
+ :param session_name: The name of the session to stop and delete.
171
+ :return: A tuple containing a boolean indicating success and an integer status code.
172
+ True, 0: If the session was stopped and deleted successfully.
173
+ False, <status>: If the session could not be stopped and deleted and the status code, why it failed.
174
+ """
175
+
176
+ session_name_unicode = ctypes.create_unicode_buffer(session_name)
177
+ properties_size = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES) + 1024 # Adjust buffer size if needed
178
+ properties = ctypes.create_string_buffer(properties_size)
179
+
180
+ trace_properties = ctypes.cast(properties, ctypes.POINTER(const.EVENT_TRACE_PROPERTIES)).contents
181
+ trace_properties.Wnode.BufferSize = properties_size
182
+ trace_properties.Wnode.Flags = const.WNODE_FLAG_TRACED_GUID
183
+ trace_properties.Wnode.Guid = const.GUID() # Ensure a GUID is provided if necessary
184
+ trace_properties.LoggerNameOffset = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
185
+
186
+ ctypes.memmove(ctypes.addressof(properties) + trace_properties.LoggerNameOffset,
187
+ session_name_unicode, ctypes.sizeof(session_name_unicode))
188
+
189
+ status = const.advapi32.ControlTraceW(
190
+ None, session_name_unicode, ctypes.byref(trace_properties), const.EVENT_TRACE_CONTROL_STOP)
191
+
192
+ if status != 0:
193
+ # print(f"Failed to stop and delete ETW session: {status}")
194
+ return False, status
195
+ else:
196
+ # print("ETW session stopped and deleted successfully.")
197
+ return True, status
198
+
199
+
200
+ @const.EVENT_CALLBACK_TYPE
201
+ def _default_callback(
202
+ event_record_ptr
203
+ ):
204
+ """
205
+ This function will be called by Windows for every incoming ETW event.
206
+ 'event_record_ptr' is a pointer to an EVENT_RECORD structure.
207
+ """
208
+ # Convert pointer to a Python EVENT_RECORD object
209
+ event_record = event_record_ptr.contents
210
+
211
+ # Do something with event_record (e.g., parse via TDH)
212
+ print("Received an ETW event!", event_record.EventHeader.ProviderId)
213
+
214
+
215
+ def start_etw_consumer(
216
+ session_name: str,
217
+ record_queue: queue.Queue
218
+ ):
219
+ """
220
+ Attach to an existing real-time ETW session by 'session_name'
221
+ using the "new" EVENT_RECORD callback approach.
222
+ This call blocks until the ETW session is stopped or an error occurs.
223
+ """
224
+ # 1) Create a closure callback that references the queue
225
+ # We define an inner function that sees 'record_queue' from outer scope.
226
+ # Then we wrap that in EVENT_CALLBACK_TYPE.
227
+ if record_queue is not None:
228
+ @const.EVENT_CALLBACK_TYPE
229
+ def _queue_callback(event_record_ptr):
230
+ event_record = event_record_ptr.contents
231
+ # # Example: put a simple dict with the provider ID & possibly more info
232
+ # record_queue.put({
233
+ # "provider_id": str(event_record.EventHeader.ProviderId),
234
+ # "process_id": event_record.EventHeader.ProcessId,
235
+ # "thread_id": event_record.EventHeader.ThreadId,
236
+ # # ... more fields or parse them with TdhGetEventInformation, etc.
237
+ # })
238
+ print("Received an ETW event!", event_record.EventHeader.ProviderId)
239
+ record_queue.put(event_record)
240
+ else:
241
+ _queue_callback = _default_callback
242
+
243
+ # Keep a reference to the callback so Python doesn't GC it.
244
+ start_etw_consumer._callback_ref = _queue_callback
245
+
246
+ # Prepare the EVENT_TRACE_LOGFILE structure
247
+ logfile = const.EVENT_TRACE_LOGFILE()
248
+ # You can also do: logfile.LoggerName = session_name
249
+ # If you assign session_name (a Python str), ctypes automatically converts it to a temporary c_wchar_p under the hood.
250
+ # logfile.LoggerName = ctypes.c_wchar_p(session_name)
251
+ logfile.LoggerName = session_name
252
+ logfile.LogFileName = None # Real-time, not from a file
253
+ logfile.ProcessTraceMode = (const.PROCESS_TRACE_MODE_REAL_TIME | const.PROCESS_TRACE_MODE_EVENT_RECORD)
254
+
255
+ # Point to our Python callback
256
+ logfile.EventRecordCallback = const.EVENT_RECORD_CALLBACK(_default_callback)
257
+
258
+ # Open the trace
259
+ trace_handle = const.OpenTrace(ctypes.byref(logfile))
260
+ if trace_handle == const.INVALID_PROCESSTRACE_HANDLE:
261
+ error_code = ctypes.get_last_error()
262
+ raise OSError(f"OpenTrace failed with error code: {error_code}")
263
+
264
+ # trace_handle is actually an unsigned long, but in 64-bit it's a 64-bit handle.
265
+ # We'll create an array of one handle for ProcessTrace:
266
+ trace_handle_array = (ctypes.c_uint64 * 1)(trace_handle)
267
+
268
+ # Blocking call - will not return until the session is stopped (or error).
269
+ status = const.ProcessTrace(
270
+ trace_handle_array,
271
+ 1, # HandleCount = 1
272
+ None, # StartTime = None
273
+ None # EndTime = None
274
+ )
275
+ if status != 0:
276
+ raise OSError(f"ProcessTrace failed with error code: {status}")
277
+
278
+ print("ProcessTrace returned. ETW consumer finished.")
279
+
280
+
281
+ def start_etw_consumer_in_thread(
282
+ session_name: str,
283
+ record_queue: queue.Queue
284
+ ):
285
+ """
286
+ Start an ETW consumer in a separate thread.
287
+ """
288
+ import threading
289
+
290
+ def _start_etw_consumer():
291
+ start_etw_consumer(session_name, record_queue)
292
+
293
+ thread = threading.Thread(target=_start_etw_consumer)
294
+ thread.start()
295
+
296
+
297
+ def get_all_providers(key_as: Literal['name', 'guid'] = 'name') -> dict:
298
+ """
299
+ Get all ETW providers available on the system.
300
+
301
+ :param key_as: The key to use in the dictionary, either 'name' or 'guid'.
302
+ 'name': The provider name is used as the key, the guid as the value.
303
+ 'guid': The provider guid is used as the key, the name as the value.
304
+ :return: dict containing the provider name and GUID.
305
+ """
306
+
307
+ if key_as not in ['name', 'guid']:
308
+ raise ValueError("key_as must be either 'name' or 'guid'")
309
+
310
+ providers_info_size = ULONG(0)
311
+ status = const.tdh.TdhEnumerateProviders(None, ctypes.byref(providers_info_size))
312
+
313
+ # Initial allocation
314
+ buffer = (ctypes.c_byte * providers_info_size.value)()
315
+ providers_info = ctypes.cast(buffer, ctypes.POINTER(const.PROVIDER_ENUMERATION_INFO))
316
+
317
+ # Loop to handle resizing
318
+ while True:
319
+ status = const.tdh.TdhEnumerateProviders(providers_info, ctypes.byref(providers_info_size))
320
+
321
+ if status == 0:
322
+ break
323
+ elif status == 0x8007007A: # ERROR_INSUFFICIENT_BUFFER
324
+ buffer = (ctypes.c_byte * providers_info_size.value)()
325
+ providers_info = ctypes.cast(buffer, ctypes.POINTER(const.PROVIDER_ENUMERATION_INFO))
326
+ else:
327
+ raise ctypes.WinError(status)
328
+
329
+ provider_count = providers_info.contents.NumberOfProviders
330
+ provider_array = ctypes.cast(
331
+ ctypes.addressof(providers_info.contents) + ctypes.sizeof(const.PROVIDER_ENUMERATION_INFO),
332
+ ctypes.POINTER(const.PROVIDER_INFORMATION * provider_count))
333
+
334
+ providers: dict = {}
335
+ for i in range(provider_count):
336
+ provider = provider_array.contents[i]
337
+ provider_name_offset = provider.ProviderNameOffset
338
+ provider_name_ptr = ctypes.cast(
339
+ ctypes.addressof(providers_info.contents) + provider_name_offset, ctypes.c_wchar_p)
340
+ provider_name = provider_name_ptr.value
341
+ provider_guid = uuid.UUID(bytes_le=bytes(provider.ProviderId))
342
+ provider_guid_string = str(provider_guid)
343
+
344
+ if key_as == 'name':
345
+ providers[provider_name] = provider_guid_string
346
+ elif key_as == 'guid':
347
+ providers[provider_guid_string] = provider_name
348
+
349
+ return providers
350
+
351
+
352
+ def list_etw_sessions() -> list[dict]:
353
+ """
354
+ List all running ETW sessions.
355
+
356
+ :return: A list of dictionaries containing the names of all running ETW sessions and their log files.
357
+ """
358
+ # Create an array of EVENT_TRACE_PROPERTIES pointers
359
+ PropertiesArrayType = ctypes.POINTER(const.EVENT_TRACE_PROPERTIES) * const.MAXIMUM_LOGGERS
360
+ properties_array = PropertiesArrayType()
361
+ for i in range(const.MAXIMUM_LOGGERS):
362
+ properties = const.EVENT_TRACE_PROPERTIES()
363
+ properties.Wnode.BufferSize = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
364
+ properties_array[i] = ctypes.pointer(properties)
365
+
366
+ # Define the number of loggers variable
367
+ logger_count = ULONG(const.MAXIMUM_LOGGERS)
368
+
369
+ # Call QueryAllTraces
370
+ status = const.QueryAllTraces(properties_array, const.MAXIMUM_LOGGERS, ctypes.byref(logger_count))
371
+ if status != 0:
372
+ raise Exception(f"QueryAllTraces failed, error code: {status}")
373
+
374
+ # Extract session names
375
+ session_list: list = []
376
+ for i in range(logger_count.value):
377
+ logger_name = None
378
+ logfile_path = None
379
+
380
+ properties = properties_array[i].contents
381
+ if properties.LoggerNameOffset != 0:
382
+ logger_name_address = ctypes.addressof(properties) + properties.LoggerNameOffset
383
+ logger_name = ctypes.wstring_at(logger_name_address)
384
+ if properties.LogFileNameOffset != 0:
385
+ logfile_name_address = ctypes.addressof(properties) + properties.LogFileNameOffset
386
+ logfile_path = ctypes.wstring_at(logfile_name_address)
387
+
388
+ session_list.append({
389
+ 'session_name': logger_name,
390
+ 'log_file': logfile_path
391
+ })
392
+
393
+ return session_list
@@ -0,0 +1,67 @@
1
+ import os
2
+ import ctypes
3
+ import pefile
4
+
5
+
6
+ def get_file_properties(file_path: str) -> dict:
7
+ """
8
+ Retrieve file version properties using ctypes.
9
+
10
+ :param file_path: Full path to the file.
11
+ :return: Dictionary with file properties.
12
+ """
13
+
14
+ def query_value(name):
15
+ r = ctypes.c_void_p()
16
+ l = ctypes.c_uint()
17
+ ctypes.windll.version.VerQueryValueW(
18
+ res, f"\\StringFileInfo\\040904b0\\{name}", ctypes.byref(r), ctypes.byref(l))
19
+ return ctypes.wstring_at(r) if r.value else "N/A"
20
+
21
+ properties = {
22
+ "FileDescription": "N/A",
23
+ "FileVersion": "N/A",
24
+ "ProductName": "N/A",
25
+ "ProductVersion": "N/A",
26
+ }
27
+
28
+ if not os.path.isfile(file_path):
29
+ return properties
30
+
31
+ # Load version information
32
+ size = ctypes.windll.version.GetFileVersionInfoSizeW(file_path, None)
33
+ if size == 0:
34
+ return properties
35
+
36
+ res = ctypes.create_string_buffer(size)
37
+ ctypes.windll.version.GetFileVersionInfoW(file_path, None, size, res)
38
+
39
+ properties["FileDescription"] = query_value("FileDescription")
40
+ properties["FileVersion"] = query_value("FileVersion")
41
+ properties["ProductName"] = query_value("ProductName")
42
+ properties["ProductVersion"] = query_value("ProductVersion")
43
+
44
+ # Fallback to pefile if ctypes fails or returns, pefile is much slower but more reliable, so we only use it as a fallback.
45
+ if properties["FileDescription"] == "N/A" or properties["FileVersion"] == "N/A":
46
+ pe = pefile.PE(file_path)
47
+ version_info = pe.VS_FIXEDFILEINFO
48
+ if version_info:
49
+ # If version_info is a list, take the first valid entry
50
+ if isinstance(version_info, list):
51
+ version_info = version_info[0]
52
+
53
+ properties["FileVersion"] = f"{version_info.FileVersionMS >> 16}.{version_info.FileVersionMS & 0xFFFF}.{version_info.FileVersionLS >> 16}.{version_info.FileVersionLS & 0xFFFF}"
54
+ # Attempt to get additional metadata
55
+ for entry in pe.FileInfo or []:
56
+ for structure in entry:
57
+ if hasattr(structure, "StringTable"):
58
+ for string_table in structure.StringTable:
59
+ for key, value in string_table.entries.items():
60
+ if key == b"FileDescription":
61
+ properties["FileDescription"] = value.decode("utf-8", errors="ignore")
62
+ elif key == b"ProductName":
63
+ properties["ProductName"] = value.decode("utf-8", errors="ignore")
64
+ elif key == b"ProductVersion":
65
+ properties["ProductVersion"] = value.decode("utf-8", errors="ignore")
66
+
67
+ return properties
@@ -1,8 +1,9 @@
1
1
  import os
2
2
  from pathlib import Path
3
3
 
4
+ from dkarchiver.arch_wrappers import sevenz_app_w
5
+
4
6
  from . import tables
5
- from ....archiver import sevenz_app_w
6
7
 
7
8
 
8
9
  def resolve_directory_path(directory_info, directory_key):
@@ -1,12 +1,12 @@
1
1
  import os
2
- import sys
3
2
  import argparse
4
3
 
4
+ from dkarchiver.arch_wrappers import sevenz_app_w
5
+
5
6
  from .base import msi
6
7
  from . import base, tables, cabs
7
8
  from ... import olefilew
8
9
  from ....print_api import print_api
9
- from ....archiver import sevenz_app_w
10
10
 
11
11
 
12
12
  # Directory names.
@@ -80,26 +80,28 @@ def extract_files_from_msi_main(
80
80
 
81
81
  # If not provided, raise an error.
82
82
  if not msi_filepath:
83
- print_api("The path to the MSI file is not provided.", color="red")
84
- sys.exit()
83
+ print_api("The path to the MSI file is not provided with [-m].", color="red")
84
+ return 1
85
85
  if not main_out_directory:
86
- print_api("The main output directory is not provided.", color="red")
86
+ print_api("The main output directory is not provided with [-o].", color="red")
87
+ return 1
87
88
  if not sevenz_path:
88
89
  print_api(
89
90
  "The path to the 7z executable is not provided. Assuming 7z is in the PATH environment variable.",
90
91
  color="yellow")
92
+ sevenz_path = "7z"
91
93
 
92
94
  if not sevenz_app_w.is_path_contains_7z_executable(sevenz_path):
93
95
  print_api("The path to 7z does not contain 7z executable", color="red")
94
- sys.exit()
96
+ return 1
95
97
 
96
- if not os.path.isfile(sevenz_path):
98
+ if sevenz_path != "7z" and not os.path.isfile(sevenz_path):
97
99
  print_api("The path to 7z executable doesn't exist.", color="red")
98
- sys.exit()
100
+ return 1
99
101
 
100
102
  if not sevenz_app_w.is_executable_a_7z(sevenz_path):
101
103
  print_api("Provided 7z executable is not 7z.", color="red")
102
- sys.exit()
104
+ return 1
103
105
 
104
106
  # Create the main output directory.
105
107
  os.makedirs(main_out_directory, exist_ok=True)
@@ -134,3 +136,5 @@ def extract_files_from_msi_main(
134
136
 
135
137
  # Extract OLE metadata from the MSI file.
136
138
  olefilew.extract_ole_metadata(msi_filepath, os.path.join(embedded_files_directory, OLE_METADATA))
139
+
140
+ return 0
@@ -417,3 +417,38 @@ def get_directory_table_info(db_handle):
417
417
  msi.MsiCloseHandle(view_handle)
418
418
 
419
419
  return directory_info
420
+
421
+
422
+ def _get_stream_table_info(db_handle):
423
+ """
424
+ Get stream table info.
425
+ Basically this function gets all the file names and their binaries from the _Streams table.
426
+ All the above functions already do this in a more structured way.
427
+ There is nothing more in this function that you will find, unless there is a file that will not be in other tables,
428
+ which is very unlikely.
429
+
430
+ The only thing that may be of interest is the '\x05SummaryInformation' stream, which is a special stream that
431
+ contains information about the MSI package. But we already use the 'wrappers.olefilew.extract_ole_metadata'
432
+ function to get this information in the parsed way.
433
+ :param db_handle:
434
+ :return:
435
+ """
436
+ query = "SELECT `Name`, `Data` FROM `_Streams`"
437
+ view_handle = base.create_open_execute_view_handle(db_handle, query)
438
+
439
+ stream_info = {}
440
+
441
+ while True:
442
+ record_handle = base.create_fetch_record_from_view_handle(view_handle)
443
+ if not record_handle:
444
+ break
445
+
446
+ stream_name = base.get_table_field_data_from_record(record_handle, field_index=1, data_type='stringw')
447
+ stream_data = base.get_table_field_data_from_record(record_handle, field_index=2, data_type='stream')
448
+
449
+ stream_info[stream_name] = stream_data
450
+
451
+ msi.MsiCloseHandle(record_handle)
452
+ msi.MsiCloseHandle(view_handle)
453
+
454
+ return stream_info