atomicshop 2.14.2__py3-none-any.whl → 2.14.3__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.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

Files changed (27) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/etws/trace.py +5 -21
  3. atomicshop/etws/traces/trace_dns.py +17 -14
  4. atomicshop/etws/traces/trace_sysmon_process_creation.py +34 -22
  5. atomicshop/get_process_list.py +133 -0
  6. atomicshop/monitor/change_monitor.py +1 -0
  7. atomicshop/monitor/checks/dns.py +2 -1
  8. atomicshop/process.py +3 -3
  9. atomicshop/process_poller/__init__.py +0 -0
  10. atomicshop/process_poller/pollers/__init__.py +0 -0
  11. atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py +95 -0
  12. atomicshop/process_poller/process_pool.py +208 -0
  13. atomicshop/process_poller/simple_process_pool.py +112 -0
  14. atomicshop/process_poller/tracer_base.py +45 -0
  15. atomicshop/process_poller/tracers/__init__.py +0 -0
  16. atomicshop/process_poller/tracers/event_log.py +46 -0
  17. atomicshop/process_poller/tracers/sysmon_etw.py +68 -0
  18. atomicshop/wrappers/pywin32w/win_event_log/subscribe.py +0 -2
  19. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -24
  20. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +103 -0
  21. {atomicshop-2.14.2.dist-info → atomicshop-2.14.3.dist-info}/METADATA +1 -1
  22. {atomicshop-2.14.2.dist-info → atomicshop-2.14.3.dist-info}/RECORD +26 -16
  23. atomicshop/process_poller.py +0 -345
  24. /atomicshop/{process_name_cmd.py → get_process_name_cmd_dll.py} +0 -0
  25. {atomicshop-2.14.2.dist-info → atomicshop-2.14.3.dist-info}/LICENSE.txt +0 -0
  26. {atomicshop-2.14.2.dist-info → atomicshop-2.14.3.dist-info}/WHEEL +0 -0
  27. {atomicshop-2.14.2.dist-info → atomicshop-2.14.3.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=z4rThLLSu1i3Wlh2xQO9kTZiAnom3boVcidQ2m4F8V0,123
1
+ atomicshop/__init__.py,sha256=oGFh-WMJw07HDbokeQe5ZNDZSUzB3jHxbH8krJL842c,123
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -16,6 +16,8 @@ atomicshop/emails.py,sha256=I0KyODQpIMEsNRi9YWSOL8EUPBiWyon3HRdIuSj3AEU,1410
16
16
  atomicshop/file_types.py,sha256=-0jzQMRlmU1AP9DARjk-HJm1tVE22E6ngP2mRblyEjY,763
17
17
  atomicshop/filesystem.py,sha256=202ue2LkjI1KdaxvB_RHV-2eIczy2-caZGLO4PSePik,53887
18
18
  atomicshop/functions.py,sha256=pK8hoCE9z61PtWCxQJsda7YAphrLH1wxU5x-1QJP-sY,499
19
+ atomicshop/get_process_list.py,sha256=hi1NOG-i8S6EcyQ6LTfP4pdxqRfjEijz9SZ5nEbcM9Q,6076
20
+ atomicshop/get_process_name_cmd_dll.py,sha256=CtaSp3mgxxJKCCVW8BLx6BJNx4giCklU_T7USiCEwfc,5162
19
21
  atomicshop/hashing.py,sha256=Le8qGFyt3_wX-zGTeQShz7L2HL_b6nVv9PnawjglyHo,3474
20
22
  atomicshop/http_parse.py,sha256=nrf2rZcprLqtW8HVrV7TCZ1iTBcWRRy-mXIlAOzcaJs,9703
21
23
  atomicshop/inspect_wrapper.py,sha256=sGRVQhrJovNygHTydqJj0hxES-aB2Eg9KbIk3G31apw,11429
@@ -25,9 +27,7 @@ atomicshop/on_exit.py,sha256=Wf1iy2e0b0Zu7oRxrct3VkLdQ_x9B32-z_JerKTt9Z0,5493
25
27
  atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erXXdjwtS4,4574
26
28
  atomicshop/permissions.py,sha256=P6tiUKV-Gw-c3ePEVsst9bqWaHJbB4ZlJB4xbDYVpEs,4436
27
29
  atomicshop/print_api.py,sha256=DhbCQd0MWZZ5GYEk4oTu1opRFC-b31g1VWZgTGewG2Y,11568
28
- atomicshop/process.py,sha256=R1BtXWjG2g2Q3WlsyhbIlXZz0UkQeagY7fQyBOIX_DM,15951
29
- atomicshop/process_name_cmd.py,sha256=CtaSp3mgxxJKCCVW8BLx6BJNx4giCklU_T7USiCEwfc,5162
30
- atomicshop/process_poller.py,sha256=17sc332AcTfSVPorjYsgRwNm75rLCqvpOQsieMwLMKU,16105
30
+ atomicshop/process.py,sha256=U2gyRl0bw2138y-rOMABMVptRvAL81ZfX1JyfxJI_Oo,15973
31
31
  atomicshop/python_file_patcher.py,sha256=kd3rBWvTcosLEk-7TycNdfKW9fZbe161iVwmH4niUo0,5515
32
32
  atomicshop/python_functions.py,sha256=zJg4ogUwECxrDD7xdDN5JikIUctITM5lsyabr_ZNsRw,4435
33
33
  atomicshop/question_answer_engine.py,sha256=DuOn7QEgKKfqZu2cR8mVeFIfFgayfBHiW-jY2VPq_Fo,841
@@ -106,10 +106,10 @@ atomicshop/etws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  atomicshop/etws/const.py,sha256=v3x_IdCYeSKbCGywiZFOZln80ldpwKW5nuMDuUe51Jg,1257
107
107
  atomicshop/etws/providers.py,sha256=fVmWi-uGdtnsQTDpu_ty6dzx0GMhGokiST73LNBEJ38,129
108
108
  atomicshop/etws/sessions.py,sha256=k3miewU278xn829cqDbsuH_bmZHPQE9-Zn-hINbxUSE,1330
109
- atomicshop/etws/trace.py,sha256=v2yA3FicR9WIg5n5KlZEuYbLAzTTjiDk7avWxcEjwp8,8439
109
+ atomicshop/etws/trace.py,sha256=KddD6_pXWhB1nr_ZsPTX1W3zayAdy5v0UxDsTcrme_w,7286
110
110
  atomicshop/etws/traces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
- atomicshop/etws/traces/trace_dns.py,sha256=-bw7JGDeAl2UtNGoSPeD_gh6ij4IGAbPdB8VWip8fXc,5960
112
- atomicshop/etws/traces/trace_sysmon_process_creation.py,sha256=WdlQiOfRZC-_PpuE6BkOw5zB0DLc3fhVrANyLrgfCac,4833
111
+ atomicshop/etws/traces/trace_dns.py,sha256=rWQ8bv8eMHBRRkA8oxO9caYqj0h4Emw4aZXmoI3Q6fg,6292
112
+ atomicshop/etws/traces/trace_sysmon_process_creation.py,sha256=OM-bkK38uYMwWLZKNOTDa0Xdk3sO6sqsxoMUIiPvm5g,4656
113
113
  atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  atomicshop/file_io/csvs.py,sha256=y8cJtnlN-NNxNupzJgSeGq9aQ4wNxYLFPX9vNNlUiIc,5830
115
115
  atomicshop/file_io/docxs.py,sha256=6tcYFGp0vRsHR47VwcRqwhdt2DQOwrAUYhrwN996n9U,5117
@@ -139,13 +139,22 @@ atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256
139
139
  atomicshop/mitm/engines/__reference_general/recorder___reference_general.py,sha256=KENDVf9OwXD9gwSh4B1XxACCe7iHYjrvnW1t6F64wdE,695
140
140
  atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha256=1AM49UaFTKA0AHw-k3SV3uH3QbG-o6ux0c-GoWkKNU0,6993
141
141
  atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
- atomicshop/monitor/change_monitor.py,sha256=dGhk5bJPxLCHa2FOVkort99E7vjVojra9GlvhpcKSqE,7551
142
+ atomicshop/monitor/change_monitor.py,sha256=K5NlVp99XIDDPnQQMdru4BDmua_DtcDIhVAzkTOvD5s,7673
143
143
  atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
- atomicshop/monitor/checks/dns.py,sha256=clslElMi2HZaO3G1nXt2t0O2Yj0vFsJd6CXRdIXCGJM,7038
144
+ atomicshop/monitor/checks/dns.py,sha256=GHBQ2GEPaU3hAmK6l2vM2PKTcBNzDDF9ThZixqnl9Qk,7108
145
145
  atomicshop/monitor/checks/file.py,sha256=2tIDSlX2KZNc_9i9ji1tcOqupbFTIOj7cKXLyBEDWMk,3263
146
146
  atomicshop/monitor/checks/network.py,sha256=CGZWl4WlQrxayZeVF9JspJXwYA-zWx8ECWTVGSlXc98,3825
147
147
  atomicshop/monitor/checks/process_running.py,sha256=x66wd6-l466r8sbRQaIli0yswyGt1dH2DVXkGDL6O0Q,1891
148
148
  atomicshop/monitor/checks/url.py,sha256=1PvKt_d7wFg7rDMFpUejAQhj0mqWsmlmrNfjNAV2G4g,4123
149
+ atomicshop/process_poller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
+ atomicshop/process_poller/process_pool.py,sha256=1CaG6Cov-Pt_kZohfFeQFT42YBnEwNBA6ge55dxok_8,9600
151
+ atomicshop/process_poller/simple_process_pool.py,sha256=hJkrn-efetLjyC8CevAFcsiKwBBKS8onYbf2ygq2J18,4540
152
+ atomicshop/process_poller/tracer_base.py,sha256=IOiHcnmF-MccOSCErixN5mve9RifZ9cPnGVHCIRchrs,1091
153
+ atomicshop/process_poller/pollers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
154
+ atomicshop/process_poller/pollers/psutil_pywin32wmi_dll.py,sha256=XRRfOIy62iOYU8IKRcyECWiL0rqQ35DeYbPsv_SHDVM,4510
155
+ atomicshop/process_poller/tracers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
+ atomicshop/process_poller/tracers/event_log.py,sha256=Ob5v18VPZ__PN8TP6aJJjQZcDMwfGA2pIKwKjrl5j3k,1417
157
+ atomicshop/process_poller/tracers/sysmon_etw.py,sha256=zn5YGX0Uro_Om7Gp1O4r0nlP1cR7BX1nYQmELPLZc9I,2500
149
158
  atomicshop/ssh_scripts/process_from_ipv4.py,sha256=uDBKZ2Ds20614JW1xMLr4IPB-z1LzIwy6QH5-SJ4j0s,1681
150
159
  atomicshop/ssh_scripts/process_from_port.py,sha256=uDTkVh4zc3HOTTGv1Et3IxM3PonDJCPuFRL6rnZDQZ4,1389
151
160
  atomicshop/startup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -251,9 +260,10 @@ atomicshop/wrappers/pywin32w/console.py,sha256=LstHajPLgXp9qQxFNR44QfH10nOnNp3bC
251
260
  atomicshop/wrappers/pywin32w/winshell.py,sha256=i2bKiMldPU7_azsD5xGQDdMwjaM7suKJd3k0Szmcs6c,723
252
261
  atomicshop/wrappers/pywin32w/wmi_win32process.py,sha256=qMzXtJ5hBZ5ydAyqpDbSx0nO2RJQL95HdmV5SzNKMhk,6826
253
262
  atomicshop/wrappers/pywin32w/win_event_log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
- atomicshop/wrappers/pywin32w/win_event_log/subscribe.py,sha256=a0SqjtUfo-fVR5jlsoygFlDrjHelOKgCBvia3suyzW8,8217
263
+ atomicshop/wrappers/pywin32w/win_event_log/subscribe.py,sha256=FYo2X0Xm3lb3GIdIt_8usoj7JPSDWj0iwsIJ4OwZLQM,8156
255
264
  atomicshop/wrappers/pywin32w/win_event_log/subscribes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py,sha256=_no1MnXjhLgOi9Y7xc7HHgTe6sjZkHIIhcVCd5BKZgM,6865
265
+ atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py,sha256=1PrPiDiuiVfzfzN5BUuxMfUoCgGW7RGgH6HVrjpTnQc,6064
266
+ atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py,sha256=0k09fiAwKDJO404bjxUWSSSLOiNANl-VTJDD_YLq-I8,3763
257
267
  atomicshop/wrappers/pywin32w/win_event_log/subscribes/schannel_logging.py,sha256=8nxIcNcbeEuvoBwhujgh7-oIpL9A6J-gg1NM8hOGAVA,3442
258
268
  atomicshop/wrappers/socketw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
259
269
  atomicshop/wrappers/socketw/accepter.py,sha256=HQC1EyZmyUtVEfFbaBkHCE-VZp6RWyd9mEqAkgsE1fk,1749
@@ -271,8 +281,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
271
281
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
272
282
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
273
283
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
274
- atomicshop-2.14.2.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
275
- atomicshop-2.14.2.dist-info/METADATA,sha256=VIgp-Go_u3KNgCb_tRAE6XcD9IyNa2lHdn0C2WFmscY,10478
276
- atomicshop-2.14.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
277
- atomicshop-2.14.2.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
278
- atomicshop-2.14.2.dist-info/RECORD,,
284
+ atomicshop-2.14.3.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
285
+ atomicshop-2.14.3.dist-info/METADATA,sha256=JhF66mFvmwPdaPxY9XiSY3ZjfBDikdaCzCF2PRNO0Ac,10478
286
+ atomicshop-2.14.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
287
+ atomicshop-2.14.3.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
288
+ atomicshop-2.14.3.dist-info/RECORD,,
@@ -1,345 +0,0 @@
1
- import threading
2
- import multiprocessing
3
- import time
4
- from typing import Literal, Union
5
- from pathlib import Path
6
-
7
- from .wrappers.pywin32w import wmi_win32process
8
- from .wrappers.pywin32w.win_event_log.subscribes import process_create
9
- from .wrappers.psutilw import psutilw
10
- from .etws.traces import trace_sysmon_process_creation
11
- from .basics import dicts
12
- from .process_name_cmd import ProcessNameCmdline
13
- from .print_api import print_api
14
-
15
-
16
- def get_process_time_tester(
17
- get_method: Literal['psutil', 'pywin32', 'process_dll'] = 'process_dll',
18
- times_to_test: int = 50
19
- ):
20
- """
21
- The function will test the time it takes to get the list of processes with different methods and cycles.
22
-
23
- :param get_method: str, The method to get the list of processes. Default is 'process_list_dll'.
24
- 'psutil': Get the list of processes by 'psutil' library. Resource intensive and slow.
25
- 'pywin32': Get the list of processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
26
- 'process_dll'. Not resource intensive and fast. Probably works only in Windows 10 x64
27
- :param times_to_test: int, how many times to test the function.
28
- """
29
-
30
- import timeit
31
-
32
- setup_code = '''
33
- from atomicshop import process_poller
34
- get_process_list = process_poller.GetProcessList(get_method=get_method, connect_on_init=True)
35
- '''
36
-
37
- test_code = '''
38
- test = get_process_list.get_processes()
39
- '''
40
-
41
- # globals need to be specified, otherwise the setup_code won't work with passed variables.
42
- times = timeit.timeit(setup=setup_code, stmt=test_code, number=times_to_test, globals=locals())
43
- print(f'Execution time: {times}')
44
-
45
-
46
- class GetProcessList:
47
- """
48
- The class is responsible for getting the list of running processes.
49
-
50
- Example of one time polling with 'pywin32' method:
51
- from atomicshop import process_poller
52
- process_list: dict = \
53
- process_poller.GetProcessList(get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
54
- """
55
- def __init__(
56
- self,
57
- get_method: Literal['psutil', 'pywin32', 'process_dll'] = 'process_dll',
58
- connect_on_init: bool = False
59
- ):
60
- """
61
- :param get_method: str, The method to get the list of processes. Default is 'process_list_dll'.
62
- 'psutil': Get the list of processes by 'psutil' library. Resource intensive and slow.
63
- 'pywin32': Get the list of processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
64
- 'process_dll'. Not resource intensive and fast. Probably works only in Windows 10 x64.
65
- :param connect_on_init: bool, if True, will connect to the service on init. 'psutil' don't need to connect.
66
- """
67
- self.get_method = get_method
68
- self.process_polling_instance = None
69
-
70
- self.connected = False
71
-
72
- if self.get_method == 'psutil':
73
- self.process_polling_instance = psutilw.PsutilProcesses()
74
- self.connected = True
75
- elif self.get_method == 'pywin32':
76
- self.process_polling_instance = wmi_win32process.Pywin32Processes()
77
- elif self.get_method == 'process_dll':
78
- self.process_polling_instance = ProcessNameCmdline()
79
-
80
- if connect_on_init:
81
- self.connect()
82
-
83
- def connect(self):
84
- """
85
- Connect to the service. 'psutil' don't need to connect.
86
- """
87
-
88
- # If the service is not connected yet. Since 'psutil' don't need to connect.
89
- if not self.connected:
90
- if self.get_method == 'pywin32':
91
- self.process_polling_instance.connect()
92
- self.connected = True
93
- elif self.get_method == 'process_dll':
94
- self.process_polling_instance.load()
95
- self.connected = True
96
-
97
- def get_processes(self, as_dict: bool = True) -> Union[list, dict]:
98
- """
99
- The function will get the list of opened processes and return it as a list of dicts.
100
-
101
- :return: dict while key is pid or list of dicts, of opened processes (depending on 'as_dict' setting).
102
- """
103
-
104
- if as_dict:
105
- if self.get_method == 'psutil':
106
- return self.process_polling_instance.get_processes_as_dict(
107
- attrs=['pid', 'name', 'cmdline'], cmdline_to_string=True)
108
- elif self.get_method == 'pywin32':
109
- processes = self.process_polling_instance.get_processes_as_dict(
110
- attrs=['ProcessId', 'Name', 'CommandLine'])
111
-
112
- # Convert the keys from WMI to the keys that are used in 'psutil'.
113
- converted_process_dict = dict()
114
- for pid, process_info in processes.items():
115
- converted_process_dict[pid] = dicts.convert_key_names(
116
- process_info, {'Name': 'name', 'CommandLine': 'cmdline'})
117
-
118
- return converted_process_dict
119
- elif self.get_method == 'process_dll':
120
- return self.process_polling_instance.get_process_details(as_dict=True)
121
- else:
122
- if self.get_method == 'psutil':
123
- return self.process_polling_instance.get_processes_as_list_of_dicts(
124
- attrs=['pid', 'name', 'cmdline'], cmdline_to_string=True)
125
- elif self.get_method == 'pywin32':
126
- processes = self.process_polling_instance.get_processes_as_list_of_dicts(
127
- attrs=['ProcessId', 'Name', 'CommandLine'])
128
-
129
- # Convert the keys from WMI to the keys that are used in 'psutil'.
130
- for process_index, process_info in enumerate(processes):
131
- processes[process_index] = dicts.convert_key_names(
132
- process_info, {'ProcessId': 'pid', 'Name': 'name', 'CommandLine': 'cmdline'})
133
-
134
- return processes
135
- elif self.get_method == 'process_dll':
136
- return self.process_polling_instance.get_process_details(as_dict=as_dict)
137
-
138
-
139
- class ProcessPollerPool:
140
- """
141
- The class is responsible for polling processes and storing them in a pool.
142
- Currently, this works with 'psutil' library and takes up to 16% of CPU on my machine.
143
- Because 'psutil' fetches 'cmdline' for each 'pid' dynamically, and it takes time and resources
144
- Later, I'll find a solution to make it more efficient.
145
- """
146
- def __init__(
147
- self,
148
- interval_seconds: Union[int, float] = 0,
149
- operation: Literal['thread', 'process'] = 'thread',
150
- poller_method: Literal['psutil', 'pywin32', 'process_dll', 'sysmon_etw', 'event_log'] = 'event_log',
151
- sysmon_etw_session_name: str = None,
152
- sysmon_directory: str = None
153
- ):
154
- """
155
- :param interval_seconds: float, how many seconds to wait between each cycle.
156
- Default is 0, which means that the polling will be as fast as possible.
157
-
158
- Basically, you want it to be '0' if you want to get the most recent processes.
159
- Any polling by itself takes time, so if you want to get the most recent processes, you want to do it as fast
160
- as possible.
161
- :param operation: str, 'thread' or 'process'. Default is 'process'.
162
- 'process': The polling will be done in a new process.
163
- 'thread': The polling will be done in a new thread.
164
-
165
- Python is slow, if you are going to use 'thread' all other operations inside this thread will be very slow.
166
- You can even get exceptions, if you're using process polling for correlations of PIDs and process names.
167
- It is advised to use the 'process' operation, which will not affect other operations in the thread.
168
- :param poller_method: str. Default is 'process_dll'. Available:
169
- 'psutil': Get the list of processes by 'psutil' library. Resource intensive and slow.
170
- 'pywin32': Get the list of processes by 'pywin32' library, using WMI. Not resource intensive, but slow.
171
- 'process_dll'. Not resource intensive and fast. Probably works only in Windows 10 x64.
172
- 'sysmon_etw': Get the list of processes with running SysMon by ETW - Event Tracing for Windows.
173
- In this case 'store_cycles' and 'interval_seconds' are irrelevant, since the ETW is real-time.
174
- Steps we take:
175
- 1. Check if SysMon is Running. If not, check if the executable exists in specified
176
- location and start it as a service.
177
- 2. Start the "Microsoft-Windows-Sysmon" ETW session.
178
- 3. Take a snapshot of current processes and their CMDs with psutil and store it in a dict.
179
- 4. Each new process creation from ETW updates the dict.
180
- 'event_log': Get the list of processes by subscribing to the Windows Event Log.
181
- Log Channel: Security, Event ID: 4688.
182
- We enable the necessary prerequisites in registry and subscribe to the event.
183
- :param sysmon_etw_session_name: str, only for 'sysmon_etw' get_method.
184
- The name of the ETW session for tracing process creation.
185
- :param sysmon_directory: str, only for 'sysmon_etw' get_method.
186
- The directory where the SysMon executable is located. If non-existed will be downloaded.
187
- ---------------------------------------------
188
- If there is an exception, ProcessPollerPool.processes will be set to the exception.
189
- While getting the processes you can use this to execute the exception:
190
-
191
- processes = ProcessPollerPool.processes
192
-
193
- if isinstance(processes, BaseException):
194
- raise processes
195
- """
196
-
197
- self.interval_seconds: float = interval_seconds
198
- self.operation: str = operation
199
- self.poller_method = poller_method
200
- self.sysmon_etw_session_name: str = sysmon_etw_session_name
201
- self.sysmon_directory: str = sysmon_directory
202
-
203
- # Current process pool.
204
- self._processes: dict = dict()
205
-
206
- # The variable is responsible to stop the thread if it is running.
207
- self._running: bool = False
208
-
209
- self._process_queue = multiprocessing.Queue()
210
- self._running_state_queue = multiprocessing.Queue()
211
-
212
- def start(self):
213
- if self.operation == 'thread':
214
- self._start_thread()
215
- elif self.operation == 'process':
216
- self._start_process()
217
- else:
218
- raise ValueError(f'Invalid operation type [{self.operation}]')
219
-
220
- thread = threading.Thread(target=self._thread_get_queue)
221
- thread.daemon = True
222
- thread.start()
223
-
224
- def stop(self):
225
- self._running = False
226
- self._running_state_queue.put(False)
227
-
228
- def get_processes(self):
229
- return self._processes
230
-
231
- def _start_thread(self):
232
- self._running = True
233
-
234
- thread = threading.Thread(
235
- target=_worker, args=(
236
- self.poller_method, self._running_state_queue, self.interval_seconds,
237
- self._process_queue, self.sysmon_etw_session_name, self.sysmon_directory,
238
- )
239
- )
240
- thread.daemon = True
241
- thread.start()
242
-
243
- def _start_process(self):
244
- self._running = True
245
- multiprocessing.Process(
246
- target=_worker, args=(
247
- self.poller_method, self._running_state_queue, self.interval_seconds,
248
- self._process_queue, self.sysmon_etw_session_name, self.sysmon_directory,
249
- )).start()
250
-
251
- def _thread_get_queue(self):
252
- while True:
253
- self._processes = self._process_queue.get()
254
-
255
-
256
- def _worker(
257
- poller_method, running_state_queue, interval_seconds, process_queue, sysmon_etw_session_name, sysmon_directory):
258
- def _worker_to_get_running_state():
259
- nonlocal running_state
260
- running_state = running_state_queue.get()
261
-
262
- running_state: bool = True
263
-
264
- thread = threading.Thread(target=_worker_to_get_running_state)
265
- thread.daemon = True
266
- thread.start()
267
-
268
- if poller_method == 'sysmon_etw':
269
- poller_instance = trace_sysmon_process_creation.SysmonProcessCreationTrace(
270
- attrs=['pid', 'original_file_name', 'command_line'],
271
- session_name=sysmon_etw_session_name,
272
- close_existing_session_name=True,
273
- sysmon_directory=sysmon_directory
274
- )
275
-
276
- # We must initiate the connection inside the thread/process, because it is not thread-safe.
277
- poller_instance.start()
278
-
279
- processes = GetProcessList(get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
280
- process_queue.put(processes)
281
- elif poller_method == 'event_log':
282
- poller_instance = process_create.ProcessCreateSubscriber()
283
- poller_instance.start()
284
-
285
- processes = GetProcessList(get_method='pywin32', connect_on_init=True).get_processes(as_dict=True)
286
- process_queue.put(processes)
287
- else:
288
- poller_instance = GetProcessList(get_method=poller_method)
289
- poller_instance.connect()
290
- processes = {}
291
-
292
- exception = None
293
- while running_state:
294
- try:
295
- if poller_method == 'sysmon_etw':
296
- # Get the current processes and reinitialize the instance of the dict.
297
- current_cycle: dict = poller_instance.emit()
298
- current_processes: dict = {int(current_cycle['pid']): {
299
- 'name': current_cycle['original_file_name'],
300
- 'cmdline': current_cycle['command_line']}
301
- }
302
- elif poller_method == 'event_log':
303
- # Get the current processes and reinitialize the instance of the dict.
304
- current_cycle: dict = poller_instance.emit()
305
- current_processes: dict = {current_cycle['pid']: {
306
- 'name': Path(current_cycle['process_name']).name,
307
- 'cmdline': current_cycle['command_line']}
308
- }
309
- else:
310
- # Get the current processes and reinitialize the instance of the dict.
311
- current_processes: dict = dict(poller_instance.get_processes())
312
-
313
- # Remove Command lines that contains only numbers, since they are useless.
314
- for pid, process_info in current_processes.items():
315
- if process_info['cmdline'].isnumeric():
316
- current_processes[pid]['cmdline'] = str()
317
- elif process_info['cmdline'] == 'Error':
318
- current_processes[pid]['cmdline'] = str()
319
-
320
- # This loop is essential for keeping the command lines.
321
- # When the process unloads from memory, the last polling will have only pid and executable name, but not
322
- # the command line. This loop will keep the command line from the previous polling if this happens.
323
- for pid, process_info in current_processes.items():
324
- if pid in processes:
325
- if processes[pid]['name'] == current_processes[pid]['name']:
326
- if current_processes[pid]['cmdline'] == '':
327
- current_processes[pid]['cmdline'] = processes[pid]['cmdline']
328
- processes.update(current_processes)
329
-
330
- process_queue.put(processes)
331
-
332
- # Since ETW is a blocking operation, we don't need to sleep in tracing pollers [sysmon_etw, event_log].
333
- if poller_method not in ['sysmon_etw', 'event_log']:
334
- time.sleep(interval_seconds)
335
- except KeyboardInterrupt as e:
336
- running_state = False
337
- exception = e
338
- except Exception as e:
339
- running_state = False
340
- exception = e
341
- print_api(f'Exception in ProcessPollerPool: {e}', color='red')
342
- raise
343
-
344
- if not running_state:
345
- process_queue.put(exception)