atomicshop 2.12.24__py3-none-any.whl → 2.12.25__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.
- atomicshop/__init__.py +1 -1
- atomicshop/etw/providers.py +5 -0
- atomicshop/etw/sessions.py +29 -0
- atomicshop/etw/trace.py +16 -1
- atomicshop/etw/trace_dns.py +17 -2
- atomicshop/monitor/checks/dns.py +1 -1
- atomicshop/wrappers/ctyping/etw_winapi/const.py +41 -4
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +91 -0
- {atomicshop-2.12.24.dist-info → atomicshop-2.12.25.dist-info}/METADATA +1 -1
- {atomicshop-2.12.24.dist-info → atomicshop-2.12.25.dist-info}/RECORD +13 -12
- {atomicshop-2.12.24.dist-info → atomicshop-2.12.25.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.12.24.dist-info → atomicshop-2.12.25.dist-info}/WHEEL +0 -0
- {atomicshop-2.12.24.dist-info → atomicshop-2.12.25.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
atomicshop/etw/sessions.py
CHANGED
|
@@ -12,3 +12,32 @@ def stop_and_delete(session_name) -> tuple[bool, int]:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
return etw_functions.stop_and_delete_etw_session(session_name)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_running_list() -> list[dict]:
|
|
18
|
+
"""
|
|
19
|
+
List all running ETW sessions.
|
|
20
|
+
|
|
21
|
+
:return: A list of strings containing the names of all running ETW sessions.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
return etw_functions.list_etw_sessions()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_session_running(session_name: str) -> bool:
|
|
28
|
+
"""
|
|
29
|
+
Check if an ETW session is running.
|
|
30
|
+
|
|
31
|
+
:param session_name: The name of the session to check.
|
|
32
|
+
:return: A boolean indicating if the session is running.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# Get all running sessions.
|
|
36
|
+
running_sessions = get_running_list()
|
|
37
|
+
|
|
38
|
+
# Check if the session is in the list of running sessions.
|
|
39
|
+
for session in running_sessions:
|
|
40
|
+
if session['session_name'] == session_name:
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
return False
|
atomicshop/etw/trace.py
CHANGED
|
@@ -5,6 +5,7 @@ import sys
|
|
|
5
5
|
import etw
|
|
6
6
|
|
|
7
7
|
from ..print_api import print_api
|
|
8
|
+
from . import sessions
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class EventTrace(etw.ETW):
|
|
@@ -13,7 +14,8 @@ class EventTrace(etw.ETW):
|
|
|
13
14
|
providers: list,
|
|
14
15
|
event_callback=None,
|
|
15
16
|
event_id_filters: list = None,
|
|
16
|
-
session_name: str = None
|
|
17
|
+
session_name: str = None,
|
|
18
|
+
close_existing_session_name: bool = True
|
|
17
19
|
):
|
|
18
20
|
"""
|
|
19
21
|
:param providers: List of tuples with provider name and provider GUID.
|
|
@@ -23,6 +25,7 @@ class EventTrace(etw.ETW):
|
|
|
23
25
|
:param event_id_filters: List of event IDs that we want to filter. If not provided, all events will be returned.
|
|
24
26
|
The default in the 'etw.ETW' method is 'None'.
|
|
25
27
|
:param session_name: The name of the session to create. If not provided, a UUID will be generated.
|
|
28
|
+
:param close_existing_session_name: Boolean to close existing session names.
|
|
26
29
|
------------------------------------------
|
|
27
30
|
You should stop the ETW tracing when you are done with it.
|
|
28
31
|
'pywintrace' module starts a new session for ETW tracing, and it will not stop the session when the script
|
|
@@ -42,6 +45,7 @@ class EventTrace(etw.ETW):
|
|
|
42
45
|
atexits.run_callable_on_exit_and_signals(EventTrace.stop)
|
|
43
46
|
"""
|
|
44
47
|
self.event_queue = queue.Queue()
|
|
48
|
+
self.close_existing_session_name: bool = close_existing_session_name
|
|
45
49
|
|
|
46
50
|
# If no callback function is provided, we will use the default one, which will put the event in the queue.
|
|
47
51
|
if not event_callback:
|
|
@@ -61,6 +65,17 @@ class EventTrace(etw.ETW):
|
|
|
61
65
|
)
|
|
62
66
|
|
|
63
67
|
def start(self):
|
|
68
|
+
# Check if the session name already exists.
|
|
69
|
+
if sessions.is_session_running(self.session_name):
|
|
70
|
+
print_api(f'ETW Session already running: {self.session_name}', color='yellow')
|
|
71
|
+
|
|
72
|
+
# Close the existing session name.
|
|
73
|
+
if self.close_existing_session_name:
|
|
74
|
+
print_api(f'Closing existing session: {self.session_name}', color='blue')
|
|
75
|
+
sessions.stop_and_delete(self.session_name)
|
|
76
|
+
else:
|
|
77
|
+
print_api(f'Using existing session: {self.session_name}', color='yellow')
|
|
78
|
+
|
|
64
79
|
try:
|
|
65
80
|
super().start()
|
|
66
81
|
except OSError as e:
|
atomicshop/etw/trace_dns.py
CHANGED
|
@@ -7,7 +7,13 @@ from ..print_api import print_api
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class DnsTrace:
|
|
10
|
-
def __init__(
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
enable_process_poller: bool = False,
|
|
13
|
+
attrs: list = None,
|
|
14
|
+
session_name: str = None,
|
|
15
|
+
close_existing_session_name: bool = True
|
|
16
|
+
):
|
|
11
17
|
"""
|
|
12
18
|
DnsTrace class use to trace DNS events from Windows Event Tracing for EventId 3008.
|
|
13
19
|
|
|
@@ -16,6 +22,14 @@ class DnsTrace:
|
|
|
16
22
|
Then DNS events will be enriched with the process name and command line from the process poller.
|
|
17
23
|
:param attrs: List of attributes to return. If None, all attributes will be returned.
|
|
18
24
|
:param session_name: The name of the session to create. If not provided, a UUID will be generated.
|
|
25
|
+
:param close_existing_session_name: Boolean to close existing session names.
|
|
26
|
+
True: if ETW session with 'session_name' exists, you will be notified and the session will be closed.
|
|
27
|
+
Then the new session with this name will be created.
|
|
28
|
+
False: if ETW session with 'session_name' exists, you will be notified and the new session will not be
|
|
29
|
+
created. Instead, the existing session will be used. If there is a buffer from the previous session,
|
|
30
|
+
you will get the events from the buffer.
|
|
31
|
+
|
|
32
|
+
-------------------------------------------------
|
|
19
33
|
|
|
20
34
|
Usage Example:
|
|
21
35
|
from atomicshop.etw import dns_trace
|
|
@@ -36,7 +50,8 @@ class DnsTrace:
|
|
|
36
50
|
providers=[(dns.ETW_DNS_INFO['provider_name'], dns.ETW_DNS_INFO['provider_guid'])],
|
|
37
51
|
# lambda x: self.event_queue.put(x),
|
|
38
52
|
event_id_filters=[dns.ETW_DNS_INFO['event_id']],
|
|
39
|
-
session_name=session_name
|
|
53
|
+
session_name=session_name,
|
|
54
|
+
close_existing_session_name=close_existing_session_name
|
|
40
55
|
)
|
|
41
56
|
|
|
42
57
|
if self.enable_process_poller:
|
atomicshop/monitor/checks/dns.py
CHANGED
|
@@ -30,7 +30,7 @@ class DnsCheck:
|
|
|
30
30
|
self.fetch_engine: DnsTrace = (
|
|
31
31
|
DnsTrace(
|
|
32
32
|
enable_process_poller=True, attrs=['name', 'cmdline', 'domain', 'query_type'],
|
|
33
|
-
session_name=self.etw_session_name)
|
|
33
|
+
session_name=self.etw_session_name, close_existing_session_name=True)
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
if self.settings['alert_always'] and self.settings['alert_about_missing_entries_after_learning']:
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import ctypes
|
|
2
2
|
from ctypes import wintypes
|
|
3
|
+
from ctypes.wintypes import ULONG
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
# Load the necessary library
|
|
6
|
-
advapi32 = ctypes.WinDLL('advapi32')
|
|
7
|
-
|
|
8
6
|
# Constants
|
|
9
7
|
EVENT_TRACE_CONTROL_STOP = 1
|
|
10
8
|
WNODE_FLAG_TRACED_GUID = 0x00020000
|
|
11
9
|
|
|
10
|
+
MAXIMUM_LOGGERS = 64
|
|
11
|
+
|
|
12
12
|
|
|
13
13
|
# Define GUID structure
|
|
14
14
|
class GUID(ctypes.Structure):
|
|
@@ -53,5 +53,42 @@ class EVENT_TRACE_PROPERTIES(ctypes.Structure):
|
|
|
53
53
|
("RealTimeBuffersLost", wintypes.ULONG),
|
|
54
54
|
("LoggerThreadId", wintypes.HANDLE),
|
|
55
55
|
("LogFileNameOffset", wintypes.ULONG),
|
|
56
|
-
("LoggerNameOffset", wintypes.ULONG)
|
|
56
|
+
("LoggerNameOffset", wintypes.ULONG),
|
|
57
|
+
# Allocate space for the names at the end of the structure
|
|
58
|
+
("_LoggerName", wintypes.WCHAR * 1024),
|
|
59
|
+
("_LogFileName", wintypes.WCHAR * 1024)
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class PROVIDER_ENUMERATION_INFO(ctypes.Structure):
|
|
64
|
+
_fields_ = [
|
|
65
|
+
("NumberOfProviders", ULONG),
|
|
66
|
+
("Reserved", ULONG),
|
|
57
67
|
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class PROVIDER_INFORMATION(ctypes.Structure):
|
|
71
|
+
_fields_ = [
|
|
72
|
+
("ProviderId", ctypes.c_byte * 16),
|
|
73
|
+
("SchemaSource", ULONG),
|
|
74
|
+
("ProviderNameOffset", ULONG),
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# Load the necessary library
|
|
79
|
+
advapi32 = ctypes.WinDLL('advapi32')
|
|
80
|
+
tdh = ctypes.windll.tdh
|
|
81
|
+
|
|
82
|
+
# Define necessary TDH functions
|
|
83
|
+
tdh.TdhEnumerateProviders.argtypes = [ctypes.POINTER(PROVIDER_ENUMERATION_INFO), ctypes.POINTER(ULONG)]
|
|
84
|
+
tdh.TdhEnumerateProviders.restype = ULONG
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Define the function prototype
|
|
88
|
+
QueryAllTraces = advapi32.QueryAllTracesW
|
|
89
|
+
QueryAllTraces.argtypes = [
|
|
90
|
+
ctypes.POINTER(ctypes.POINTER(EVENT_TRACE_PROPERTIES)),
|
|
91
|
+
wintypes.ULONG,
|
|
92
|
+
ctypes.POINTER(wintypes.ULONG)
|
|
93
|
+
]
|
|
94
|
+
QueryAllTraces.restype = wintypes.ULONG
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import ctypes
|
|
2
|
+
from ctypes.wintypes import ULONG
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
2
5
|
from . import const
|
|
3
6
|
|
|
4
7
|
|
|
@@ -35,3 +38,91 @@ def stop_and_delete_etw_session(session_name) -> tuple[bool, int]:
|
|
|
35
38
|
else:
|
|
36
39
|
# print("ETW session stopped and deleted successfully.")
|
|
37
40
|
return True, status
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_all_providers() -> list[tuple[any, uuid.UUID]]:
|
|
44
|
+
"""
|
|
45
|
+
Get all ETW providers available on the system.
|
|
46
|
+
|
|
47
|
+
:return: A list of tuples containing the provider name and GUID.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
providers_info_size = ULONG(0)
|
|
51
|
+
status = const.tdh.TdhEnumerateProviders(None, ctypes.byref(providers_info_size))
|
|
52
|
+
|
|
53
|
+
# Initial allocation
|
|
54
|
+
buffer = (ctypes.c_byte * providers_info_size.value)()
|
|
55
|
+
providers_info = ctypes.cast(buffer, ctypes.POINTER(const.PROVIDER_ENUMERATION_INFO))
|
|
56
|
+
|
|
57
|
+
# Loop to handle resizing
|
|
58
|
+
while True:
|
|
59
|
+
status = const.tdh.TdhEnumerateProviders(providers_info, ctypes.byref(providers_info_size))
|
|
60
|
+
|
|
61
|
+
if status == 0:
|
|
62
|
+
break
|
|
63
|
+
elif status == 0x8007007A: # ERROR_INSUFFICIENT_BUFFER
|
|
64
|
+
buffer = (ctypes.c_byte * providers_info_size.value)()
|
|
65
|
+
providers_info = ctypes.cast(buffer, ctypes.POINTER(const.PROVIDER_ENUMERATION_INFO))
|
|
66
|
+
else:
|
|
67
|
+
raise ctypes.WinError(status)
|
|
68
|
+
|
|
69
|
+
provider_count = providers_info.contents.NumberOfProviders
|
|
70
|
+
provider_array = ctypes.cast(
|
|
71
|
+
ctypes.addressof(providers_info.contents) + ctypes.sizeof(const.PROVIDER_ENUMERATION_INFO),
|
|
72
|
+
ctypes.POINTER(const.PROVIDER_INFORMATION * provider_count))
|
|
73
|
+
|
|
74
|
+
providers = []
|
|
75
|
+
for i in range(provider_count):
|
|
76
|
+
provider = provider_array.contents[i]
|
|
77
|
+
provider_name_offset = provider.ProviderNameOffset
|
|
78
|
+
provider_name_ptr = ctypes.cast(
|
|
79
|
+
ctypes.addressof(providers_info.contents) + provider_name_offset, ctypes.c_wchar_p)
|
|
80
|
+
provider_name = provider_name_ptr.value
|
|
81
|
+
provider_guid = uuid.UUID(bytes_le=bytes(provider.ProviderId))
|
|
82
|
+
providers.append((provider_name, provider_guid))
|
|
83
|
+
|
|
84
|
+
return providers
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def list_etw_sessions() -> list[dict]:
|
|
88
|
+
"""
|
|
89
|
+
List all running ETW sessions.
|
|
90
|
+
|
|
91
|
+
:return: A list of dictionaries containing the names of all running ETW sessions and their log files.
|
|
92
|
+
"""
|
|
93
|
+
# Create an array of EVENT_TRACE_PROPERTIES pointers
|
|
94
|
+
PropertiesArrayType = ctypes.POINTER(const.EVENT_TRACE_PROPERTIES) * const.MAXIMUM_LOGGERS
|
|
95
|
+
properties_array = PropertiesArrayType()
|
|
96
|
+
for i in range(const.MAXIMUM_LOGGERS):
|
|
97
|
+
properties = const.EVENT_TRACE_PROPERTIES()
|
|
98
|
+
properties.Wnode.BufferSize = ctypes.sizeof(const.EVENT_TRACE_PROPERTIES)
|
|
99
|
+
properties_array[i] = ctypes.pointer(properties)
|
|
100
|
+
|
|
101
|
+
# Define the number of loggers variable
|
|
102
|
+
logger_count = ULONG(const.MAXIMUM_LOGGERS)
|
|
103
|
+
|
|
104
|
+
# Call QueryAllTraces
|
|
105
|
+
status = const.QueryAllTraces(properties_array, const.MAXIMUM_LOGGERS, ctypes.byref(logger_count))
|
|
106
|
+
if status != 0:
|
|
107
|
+
raise Exception(f"QueryAllTraces failed, error code: {status}")
|
|
108
|
+
|
|
109
|
+
# Extract session names
|
|
110
|
+
session_list: list = []
|
|
111
|
+
for i in range(logger_count.value):
|
|
112
|
+
logger_name = None
|
|
113
|
+
logfile_path = None
|
|
114
|
+
|
|
115
|
+
properties = properties_array[i].contents
|
|
116
|
+
if properties.LoggerNameOffset != 0:
|
|
117
|
+
logger_name_address = ctypes.addressof(properties) + properties.LoggerNameOffset
|
|
118
|
+
logger_name = ctypes.wstring_at(logger_name_address)
|
|
119
|
+
if properties.LogFileNameOffset != 0:
|
|
120
|
+
logfile_name_address = ctypes.addressof(properties) + properties.LogFileNameOffset
|
|
121
|
+
logfile_path = ctypes.wstring_at(logfile_name_address)
|
|
122
|
+
|
|
123
|
+
session_list.append({
|
|
124
|
+
'session_name': logger_name,
|
|
125
|
+
'log_file': logfile_path
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return session_list
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=i0XAOpCKxA2g_BNalWhe-R5FvF3ui38CZEoR2TgPwIg,124
|
|
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
|
|
@@ -102,9 +102,10 @@ atomicshop/basics/threads.py,sha256=xvgdDJdmgN0wmmARoZ-H7Kvl1GOcEbvgaeGL4M3Hcx8,
|
|
|
102
102
|
atomicshop/basics/timeit_template.py,sha256=fYLrk-X_dhdVtnPU22tarrhhvlggeW6FdKCXM8zkX68,405
|
|
103
103
|
atomicshop/basics/tracebacks.py,sha256=cNfh_oAwF55kSIdqtv3boHZQIoQI8TajxkTnwJwpweI,535
|
|
104
104
|
atomicshop/etw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
105
|
-
atomicshop/etw/
|
|
106
|
-
atomicshop/etw/
|
|
107
|
-
atomicshop/etw/
|
|
105
|
+
atomicshop/etw/providers.py,sha256=fVmWi-uGdtnsQTDpu_ty6dzx0GMhGokiST73LNBEJ38,129
|
|
106
|
+
atomicshop/etw/sessions.py,sha256=k3miewU278xn829cqDbsuH_bmZHPQE9-Zn-hINbxUSE,1330
|
|
107
|
+
atomicshop/etw/trace.py,sha256=tjBvV4RxCSn8Aoxj8NVxmqHekdECne_y4Zv2lbh11ak,5341
|
|
108
|
+
atomicshop/etw/trace_dns.py,sha256=f4homrWp4qMrmjC9UrEWjr9p9MrUg7SwOVbE22_IYgw,6728
|
|
108
109
|
atomicshop/file_io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
109
110
|
atomicshop/file_io/csvs.py,sha256=y8cJtnlN-NNxNupzJgSeGq9aQ4wNxYLFPX9vNNlUiIc,5830
|
|
110
111
|
atomicshop/file_io/docxs.py,sha256=6tcYFGp0vRsHR47VwcRqwhdt2DQOwrAUYhrwN996n9U,5117
|
|
@@ -136,7 +137,7 @@ atomicshop/mitm/engines/__reference_general/responder___reference_general.py,sha
|
|
|
136
137
|
atomicshop/monitor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
137
138
|
atomicshop/monitor/change_monitor.py,sha256=dGhk5bJPxLCHa2FOVkort99E7vjVojra9GlvhpcKSqE,7551
|
|
138
139
|
atomicshop/monitor/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
139
|
-
atomicshop/monitor/checks/dns.py,sha256=
|
|
140
|
+
atomicshop/monitor/checks/dns.py,sha256=YLCFoolq35dnzdnBnfUA2ag-4HfvzcHfRdm3mzX8R8o,7184
|
|
140
141
|
atomicshop/monitor/checks/file.py,sha256=2tIDSlX2KZNc_9i9ji1tcOqupbFTIOj7cKXLyBEDWMk,3263
|
|
141
142
|
atomicshop/monitor/checks/network.py,sha256=CGZWl4WlQrxayZeVF9JspJXwYA-zWx8ECWTVGSlXc98,3825
|
|
142
143
|
atomicshop/monitor/checks/process_running.py,sha256=x66wd6-l466r8sbRQaIli0yswyGt1dH2DVXkGDL6O0Q,1891
|
|
@@ -169,8 +170,8 @@ atomicshop/wrappers/certauthw/certauthw.py,sha256=4WvhjANI7Kzqrr_nKmtA8Kf7B6rute
|
|
|
169
170
|
atomicshop/wrappers/ctyping/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
170
171
|
atomicshop/wrappers/ctyping/process_winapi.py,sha256=QcXL-ETtlSSkoT8F7pYle97ubGWsjYp8cx8HxkVMgAc,2762
|
|
171
172
|
atomicshop/wrappers/ctyping/etw_winapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
172
|
-
atomicshop/wrappers/ctyping/etw_winapi/const.py,sha256
|
|
173
|
-
atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py,sha256=
|
|
173
|
+
atomicshop/wrappers/ctyping/etw_winapi/const.py,sha256=YrAyXBamAmHoQgha7oXwH9g_EqeLYXRGPderuu9FRI8,2765
|
|
174
|
+
atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py,sha256=3DLVXpTeOyTND35T_dKGzKnlLVQ0R3zt3AEcW2bNLNc,5304
|
|
174
175
|
atomicshop/wrappers/ctyping/msi_windows_installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
175
176
|
atomicshop/wrappers/ctyping/msi_windows_installer/base.py,sha256=Uu9SlWLsQQ6mjE-ek-ptHcmgiI3Ruah9bdZus70EaVY,4884
|
|
176
177
|
atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py,sha256=htzwb2ROYI8yyc82xApStckPS2yCcoyaw32yC15KROs,3285
|
|
@@ -259,8 +260,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
|
|
|
259
260
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
|
|
260
261
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
|
|
261
262
|
atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
|
|
262
|
-
atomicshop-2.12.
|
|
263
|
-
atomicshop-2.12.
|
|
264
|
-
atomicshop-2.12.
|
|
265
|
-
atomicshop-2.12.
|
|
266
|
-
atomicshop-2.12.
|
|
263
|
+
atomicshop-2.12.25.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
264
|
+
atomicshop-2.12.25.dist-info/METADATA,sha256=9B-DBn3RCaFkRPibGwrKm8FOiWIRdqjSifbGjXpi9z4,10479
|
|
265
|
+
atomicshop-2.12.25.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
266
|
+
atomicshop-2.12.25.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
267
|
+
atomicshop-2.12.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|