atomicshop 2.16.44a1__py3-none-any.whl → 2.16.45__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/archiver/search_in_archive.py +43 -9
- atomicshop/basics/classes.py +60 -1
- atomicshop/mitm/connection_thread_worker.py +53 -1
- atomicshop/mitm/engines/__parent/recorder___parent.py +3 -3
- atomicshop/websocket_parse.py +98 -70
- atomicshop/wrappers/factw/install/install_after_restart.py +2 -2
- atomicshop/wrappers/socketw/statistics_csv.py +7 -2
- atomicshop/wrappers/winregw/winreg_network.py +1 -1
- {atomicshop-2.16.44a1.dist-info → atomicshop-2.16.45.dist-info}/METADATA +1 -1
- {atomicshop-2.16.44a1.dist-info → atomicshop-2.16.45.dist-info}/RECORD +14 -14
- {atomicshop-2.16.44a1.dist-info → atomicshop-2.16.45.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.16.44a1.dist-info → atomicshop-2.16.45.dist-info}/WHEEL +0 -0
- {atomicshop-2.16.44a1.dist-info → atomicshop-2.16.45.dist-info}/top_level.txt +0 -0
atomicshop/__init__.py
CHANGED
|
@@ -65,8 +65,9 @@ def _handle_callback_matching(
|
|
|
65
65
|
callback_result = callback(archived_file_bytes)
|
|
66
66
|
if callback_result:
|
|
67
67
|
# Initialize key for callback function name if not present
|
|
68
|
-
if callback
|
|
69
|
-
results[callback
|
|
68
|
+
if _get_callback_name(callback) not in results:
|
|
69
|
+
results[_get_callback_name(callback)]: dict = {}
|
|
70
|
+
results[_get_callback_name(callback)]['files']: list = []
|
|
70
71
|
|
|
71
72
|
if archive_type == 'zip':
|
|
72
73
|
file_info = {
|
|
@@ -82,7 +83,8 @@ def _handle_callback_matching(
|
|
|
82
83
|
'size': item.uncompressed,
|
|
83
84
|
'modified_time': item.creationtime
|
|
84
85
|
}
|
|
85
|
-
results[callback
|
|
86
|
+
results[_get_callback_name(callback)]['files'].append(file_info)
|
|
87
|
+
results[_get_callback_name(callback)]['callable_result'] = callback_result
|
|
86
88
|
if return_first_only:
|
|
87
89
|
found_set.add(item.filename)
|
|
88
90
|
return True
|
|
@@ -149,11 +151,42 @@ def _search_in_archive(
|
|
|
149
151
|
break # All files found, stop searching
|
|
150
152
|
|
|
151
153
|
|
|
152
|
-
def
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
def _get_callback_name(callback: callable) -> str:
|
|
155
|
+
"""
|
|
156
|
+
Get the name of the callback function.
|
|
157
|
+
:param callback: callable.
|
|
158
|
+
:return: string, the name of the callback function. If the function is part of the class, the name will be
|
|
159
|
+
'class_name.function_name', if not the only the function name will be returned.
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
class_name = callback.__self__.__class__.__name__
|
|
163
|
+
except AttributeError:
|
|
164
|
+
class_name = None
|
|
165
|
+
|
|
166
|
+
function_name = callback.__name__
|
|
167
|
+
|
|
168
|
+
if class_name:
|
|
169
|
+
return f"{class_name}.{function_name}"
|
|
155
170
|
else:
|
|
156
|
-
return
|
|
171
|
+
return function_name
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# def _initialize_results(callback_functions: list[callable]) -> dict:
|
|
175
|
+
# """
|
|
176
|
+
# Initialize the results dictionary.
|
|
177
|
+
# :param callback_functions:
|
|
178
|
+
# :return:
|
|
179
|
+
# """
|
|
180
|
+
# if callback_functions:
|
|
181
|
+
# result_dict: dict = {}
|
|
182
|
+
# for callback in callback_functions:
|
|
183
|
+
# result_dict[_get_callback_name(callback)]: dict = {}
|
|
184
|
+
# result_dict[_get_callback_name(callback)]['files']: list = []
|
|
185
|
+
# result_dict[_get_callback_name(callback)]['callable_result']: any = None
|
|
186
|
+
#
|
|
187
|
+
# return result_dict
|
|
188
|
+
# else:
|
|
189
|
+
# return {}
|
|
157
190
|
|
|
158
191
|
|
|
159
192
|
def _get_archive_type(file_object) -> Union[str, None]:
|
|
@@ -212,7 +245,7 @@ def search_file_in_archive(
|
|
|
212
245
|
recursive: bool = False,
|
|
213
246
|
callback_functions: list = None,
|
|
214
247
|
extract_file_to_path: str = None
|
|
215
|
-
) -> dict[
|
|
248
|
+
) -> dict[list[bytes], str]:
|
|
216
249
|
"""
|
|
217
250
|
Function searches for the file names inside the zip file and returns a dictionary where the keys are the
|
|
218
251
|
names of the callback functions and the values are lists of found file bytes.
|
|
@@ -236,7 +269,8 @@ def search_file_in_archive(
|
|
|
236
269
|
raise ValueError("Either file_names_to_search or callback_functions must be provided.")
|
|
237
270
|
|
|
238
271
|
# Initialize results dictionary.
|
|
239
|
-
results = _initialize_results(callback_functions)
|
|
272
|
+
# results = _initialize_results(callback_functions)
|
|
273
|
+
results: dict[list[bytes], str] = {}
|
|
240
274
|
found_set = set()
|
|
241
275
|
|
|
242
276
|
_search_archive_content(
|
atomicshop/basics/classes.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import ast
|
|
4
|
-
import importlib
|
|
5
4
|
from pathlib import Path
|
|
5
|
+
import pkgutil
|
|
6
|
+
import importlib
|
|
7
|
+
import inspect
|
|
6
8
|
|
|
7
9
|
from ..file_io.file_io import read_file
|
|
8
10
|
|
|
@@ -26,6 +28,63 @@ class ParserParent:
|
|
|
26
28
|
"""
|
|
27
29
|
|
|
28
30
|
|
|
31
|
+
def get_list_of_classes_in_module(
|
|
32
|
+
imported_package,
|
|
33
|
+
imported_base_class
|
|
34
|
+
) -> list:
|
|
35
|
+
"""
|
|
36
|
+
Function that returns a list of classes that are subclasses of the imported_base_class from the imported_package.
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
# Package structure:
|
|
40
|
+
# unpackers
|
|
41
|
+
# ├── __init__.py
|
|
42
|
+
# ├── unpacker_base.py
|
|
43
|
+
# ├── unpacker_1.py
|
|
44
|
+
# ├── unpacker_2.py
|
|
45
|
+
# ├── unpacker_3.py
|
|
46
|
+
# └── ... (other unpacker modules)
|
|
47
|
+
|
|
48
|
+
# unpacker_base.py:
|
|
49
|
+
from abc import abstractmethod
|
|
50
|
+
class Unpacker:
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def unpack(self, file_path):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
# unpacker_1.py:
|
|
56
|
+
from unpackers.unpacker_base import Unpacker
|
|
57
|
+
class Unpacker1(Unpacker):
|
|
58
|
+
def unpack(self, file_path):
|
|
59
|
+
print(f"Unpacking file with Unpacker1: {file_path}")
|
|
60
|
+
|
|
61
|
+
# main_script.py:
|
|
62
|
+
# Import the base class
|
|
63
|
+
from unpackers.unpacker_base import Unpacker
|
|
64
|
+
# Import the package
|
|
65
|
+
import unpackers
|
|
66
|
+
# Get the list of classes
|
|
67
|
+
unpacker_classes = get_list_of_classes_in_module(imported_package=unpackers, imported_base_class=Unpacker)
|
|
68
|
+
|
|
69
|
+
:param imported_package:
|
|
70
|
+
:param imported_base_class:
|
|
71
|
+
:return:
|
|
72
|
+
"""
|
|
73
|
+
unpacker_classes = []
|
|
74
|
+
|
|
75
|
+
# Iterate over all modules in the 'imported_package' package
|
|
76
|
+
for loader, module_name, is_pkg in pkgutil.iter_modules(imported_package.__path__):
|
|
77
|
+
# Import the module
|
|
78
|
+
module = importlib.import_module(f"{imported_package.__name__}.{module_name}")
|
|
79
|
+
|
|
80
|
+
# Inspect module members to find Unpacker subclasses
|
|
81
|
+
for name, obj in inspect.getmembers(module, inspect.isclass):
|
|
82
|
+
if issubclass(obj, imported_base_class) and obj is not imported_base_class:
|
|
83
|
+
unpacker_classes.append(obj)
|
|
84
|
+
|
|
85
|
+
return unpacker_classes
|
|
86
|
+
|
|
87
|
+
|
|
29
88
|
def create_empty_class():
|
|
30
89
|
"""
|
|
31
90
|
Function creates empty class, you can add parameters to it dynamically.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
3
|
from ..wrappers.socketw import receiver, sender, socket_client, base
|
|
4
|
+
from .. import websocket_parse
|
|
4
5
|
from ..http_parse import HTTPRequestParse, HTTPResponseParse
|
|
5
6
|
from ..basics import threads, tracebacks
|
|
6
7
|
from ..print_api import print_api
|
|
@@ -65,6 +66,7 @@ def thread_worker_main(
|
|
|
65
66
|
raise ValueError(f"Error in statistics error list. Values: {statistics_error_list}")
|
|
66
67
|
|
|
67
68
|
statistics_writer.write_row(
|
|
69
|
+
thread_id=str(thread_id),
|
|
68
70
|
host=client_message.server_name,
|
|
69
71
|
tls_type=tls_type,
|
|
70
72
|
tls_version=tls_version,
|
|
@@ -92,12 +94,15 @@ def thread_worker_main(
|
|
|
92
94
|
|
|
93
95
|
def parse_http():
|
|
94
96
|
nonlocal error_message
|
|
97
|
+
nonlocal protocol
|
|
95
98
|
# Parsing the raw bytes as HTTP.
|
|
96
99
|
request_decoded, is_http_request, request_parsing_info, request_parsing_error = (
|
|
97
100
|
HTTPRequestParse(client_message.request_raw_bytes).parse())
|
|
98
101
|
|
|
99
102
|
if is_http_request:
|
|
100
|
-
|
|
103
|
+
if protocol == '':
|
|
104
|
+
protocol = 'HTTP'
|
|
105
|
+
|
|
101
106
|
client_message.request_raw_decoded = request_decoded
|
|
102
107
|
print_api(request_parsing_info, logger=network_logger, logger_method='info')
|
|
103
108
|
network_logger.info(f"Method: {request_decoded.command} | Path: {request_decoded.path}")
|
|
@@ -107,6 +112,29 @@ def thread_worker_main(
|
|
|
107
112
|
# statistics_error_list.append(error_message)
|
|
108
113
|
print_api(request_parsing_error, logger=network_logger, logger_method='error', color='yellow')
|
|
109
114
|
|
|
115
|
+
is_http_request_a_websocket()
|
|
116
|
+
|
|
117
|
+
def is_http_request_a_websocket():
|
|
118
|
+
nonlocal protocol
|
|
119
|
+
|
|
120
|
+
if protocol == 'HTTP':
|
|
121
|
+
if (client_message.request_raw_decoded and
|
|
122
|
+
hasattr(client_message.request_raw_decoded, 'headers') and
|
|
123
|
+
'Upgrade' in client_message.request_raw_decoded.headers):
|
|
124
|
+
if client_message.request_raw_decoded.headers['Upgrade'] == 'websocket':
|
|
125
|
+
protocol = 'Websocket'
|
|
126
|
+
|
|
127
|
+
network_logger.info(f'Protocol upgraded to Websocket')
|
|
128
|
+
|
|
129
|
+
def parse_websocket(raw_bytes):
|
|
130
|
+
is_deflated = websocket_parse.is_frame_deflated(raw_bytes)
|
|
131
|
+
request_decoded = websocket_frame_parser.parse_frame_bytes(raw_bytes)
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
'is_deflated': is_deflated,
|
|
135
|
+
'frame': request_decoded
|
|
136
|
+
}
|
|
137
|
+
|
|
110
138
|
def finish_thread():
|
|
111
139
|
# At this stage there could be several times that the same socket was used to the service server - we need to
|
|
112
140
|
# close this socket as well if it still opened.
|
|
@@ -141,6 +169,13 @@ def thread_worker_main(
|
|
|
141
169
|
thread_id = threads.current_thread_id()
|
|
142
170
|
client_message.thread_id = thread_id
|
|
143
171
|
|
|
172
|
+
protocol: str = str()
|
|
173
|
+
# # This is Client Masked Frame Parser.
|
|
174
|
+
# websocket_masked_frame_parser = websocket_parse.WebsocketFrameParser()
|
|
175
|
+
# # This is Server UnMasked Frame Parser.
|
|
176
|
+
# websocket_unmasked_frame_parser = websocket_parse.WebsocketFrameParser()
|
|
177
|
+
websocket_frame_parser = websocket_parse.WebsocketFrameParser()
|
|
178
|
+
|
|
144
179
|
# Loading parser by domain, if there is no parser for current domain - general reference parser is loaded.
|
|
145
180
|
# These should be outside any loop and initialized only once entering the thread.
|
|
146
181
|
parser, responder, recorder = assign_class_by_domain(
|
|
@@ -201,6 +236,12 @@ def thread_worker_main(
|
|
|
201
236
|
client_message.request_raw_bytes = client_received_raw_data
|
|
202
237
|
|
|
203
238
|
parse_http()
|
|
239
|
+
if protocol != '':
|
|
240
|
+
client_message.protocol = protocol
|
|
241
|
+
|
|
242
|
+
# Parse websocket frames only if it is not the first protocol upgrade request.
|
|
243
|
+
if protocol == 'Websocket' and cycle_count != 0:
|
|
244
|
+
client_message.request_raw_decoded = parse_websocket(client_message.request_raw_bytes)
|
|
204
245
|
|
|
205
246
|
# Custom parser, should parse HTTP body or the whole message if not HTTP.
|
|
206
247
|
parser_instance = parser(client_message)
|
|
@@ -219,6 +260,12 @@ def thread_worker_main(
|
|
|
219
260
|
# Re-initiate the 'client_message.response_list_of_raw_bytes' list, since we'll be appending
|
|
220
261
|
# new entries for empty list.
|
|
221
262
|
client_message.response_list_of_raw_bytes = list()
|
|
263
|
+
|
|
264
|
+
# If it's the first cycle and the protocol is Websocket, then we'll create the HTTP Handshake
|
|
265
|
+
# response automatically.
|
|
266
|
+
if protocol == 'Websocket' and cycle_count == 0:
|
|
267
|
+
client_message.response_list_of_raw_bytes.append(
|
|
268
|
+
websocket_parse.create_byte_http_response(client_message.request_raw_bytes))
|
|
222
269
|
# Creating response for parsed message and printing
|
|
223
270
|
responder.create_response(client_message)
|
|
224
271
|
|
|
@@ -283,9 +330,14 @@ def thread_worker_main(
|
|
|
283
330
|
|
|
284
331
|
if is_http_response:
|
|
285
332
|
client_message.response_list_of_raw_decoded.append(response_raw_decoded)
|
|
333
|
+
elif protocol == 'Websocket' and cycle_count != 0:
|
|
334
|
+
response_decoded = parse_websocket(response_raw_bytes)
|
|
335
|
+
client_message.response_list_of_raw_decoded.append(response_decoded)
|
|
286
336
|
else:
|
|
287
337
|
client_message.response_list_of_raw_decoded.append(None)
|
|
288
338
|
|
|
339
|
+
|
|
340
|
+
|
|
289
341
|
# So if the socket was closed and there was an error we can break the loop
|
|
290
342
|
if not service_ssl_socket:
|
|
291
343
|
record_and_statistics_write()
|
|
@@ -47,9 +47,9 @@ class RecorderParent:
|
|
|
47
47
|
self.build_record_path_to_engine()
|
|
48
48
|
|
|
49
49
|
# If HTTP Path is not defined, 'http_path' will be empty, and it will not interfere with file name.
|
|
50
|
-
self.record_file_path: str =
|
|
51
|
-
self.engine_record_path
|
|
52
|
-
|
|
50
|
+
self.record_file_path: str = (
|
|
51
|
+
f"{self.engine_record_path}{os.sep}{day_time_format}_"
|
|
52
|
+
f"{self.class_client_message.server_name}{self.file_extension}")
|
|
53
53
|
|
|
54
54
|
def convert_messages(self):
|
|
55
55
|
"""
|
atomicshop/websocket_parse.py
CHANGED
|
@@ -59,78 +59,106 @@ def create_byte_http_response(
|
|
|
59
59
|
raise ValueError("The event is not a Request object.")
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
except Exception as e:
|
|
75
|
-
raise e # Re-raise exceptions to be handled by the caller
|
|
76
|
-
|
|
77
|
-
# Function to parse frames
|
|
78
|
-
def parse_frame(mask: bool):
|
|
79
|
-
try:
|
|
80
|
-
# Use Frame.parse to parse the frame
|
|
81
|
-
frame_parser = Frame.parse(
|
|
82
|
-
read_exact,
|
|
83
|
-
mask=mask, # Client frames are masked
|
|
84
|
-
max_size=None,
|
|
85
|
-
extensions=[permessage_deflate], # Include the extension unconditionally
|
|
86
|
-
)
|
|
87
|
-
current_frame = run_coroutine(frame_parser)
|
|
88
|
-
except EOFError as e:
|
|
89
|
-
# Not enough data to parse a complete frame
|
|
90
|
-
raise e
|
|
91
|
-
except (ProtocolError, PayloadTooBig) as e:
|
|
92
|
-
print("Error parsing frame:", e)
|
|
93
|
-
raise e
|
|
94
|
-
except Exception as e:
|
|
95
|
-
print("Error parsing frame:", e)
|
|
96
|
-
raise e
|
|
97
|
-
return current_frame
|
|
98
|
-
|
|
99
|
-
def process_frame(current_frame):
|
|
100
|
-
if current_frame.opcode == Opcode.TEXT:
|
|
101
|
-
message = current_frame.data.decode('utf-8', errors='replace')
|
|
102
|
-
return message
|
|
103
|
-
elif current_frame.opcode == Opcode.BINARY:
|
|
104
|
-
return current_frame.data
|
|
105
|
-
elif current_frame.opcode == Opcode.CLOSE:
|
|
106
|
-
print("Received close frame")
|
|
107
|
-
elif current_frame.opcode == Opcode.PING:
|
|
108
|
-
print("Received ping")
|
|
109
|
-
elif current_frame.opcode == Opcode.PONG:
|
|
110
|
-
print("Received pong")
|
|
111
|
-
else:
|
|
112
|
-
raise WebsocketParseWrongOpcode("Received unknown frame with opcode:", current_frame.opcode)
|
|
113
|
-
|
|
114
|
-
# Create the StreamReader instance
|
|
115
|
-
reader = StreamReader()
|
|
116
|
-
|
|
117
|
-
# Instantiate the permessage-deflate extension
|
|
118
|
-
permessage_deflate = PerMessageDeflate(
|
|
119
|
-
remote_no_context_takeover=False,
|
|
120
|
-
local_no_context_takeover=False,
|
|
121
|
-
remote_max_window_bits=15,
|
|
122
|
-
local_max_window_bits=15,
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
masked = is_frame_masked(data_bytes)
|
|
62
|
+
class WebsocketFrameParser:
|
|
63
|
+
def __init__(self):
|
|
64
|
+
# Instantiate the permessage-deflate extension.
|
|
65
|
+
# If a frame uses 'deflate', then the 'permessage_deflate' should be the same object during parsing of
|
|
66
|
+
# several message on the same socket. Each time 'PerMessageDeflate' is initiated, the context changes
|
|
67
|
+
# and more than one message can't be parsed.
|
|
68
|
+
self.permessage_deflate_masked = PerMessageDeflate(
|
|
69
|
+
remote_no_context_takeover=False,
|
|
70
|
+
local_no_context_takeover=False,
|
|
71
|
+
remote_max_window_bits=15,
|
|
72
|
+
local_max_window_bits=15,
|
|
73
|
+
)
|
|
126
74
|
|
|
127
|
-
|
|
128
|
-
|
|
75
|
+
# We need separate instances for masked (frames from client) and unmasked (frames from server).
|
|
76
|
+
self.permessage_deflate_unmasked = PerMessageDeflate(
|
|
77
|
+
remote_no_context_takeover=False,
|
|
78
|
+
local_no_context_takeover=False,
|
|
79
|
+
remote_max_window_bits=15,
|
|
80
|
+
local_max_window_bits=15,
|
|
81
|
+
)
|
|
129
82
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
83
|
+
def parse_frame_bytes(
|
|
84
|
+
self,
|
|
85
|
+
data_bytes: bytes
|
|
86
|
+
):
|
|
87
|
+
# Define the read_exact function
|
|
88
|
+
def read_exact(n: int) -> Generator[None, None, bytes]:
|
|
89
|
+
return reader.read_exact(n)
|
|
90
|
+
|
|
91
|
+
# Helper function to run generator-based coroutines
|
|
92
|
+
def run_coroutine(coroutine):
|
|
93
|
+
try:
|
|
94
|
+
while True:
|
|
95
|
+
next(coroutine)
|
|
96
|
+
except StopIteration as e:
|
|
97
|
+
return e.value
|
|
98
|
+
except Exception as e:
|
|
99
|
+
raise e # Re-raise exceptions to be handled by the caller
|
|
100
|
+
|
|
101
|
+
# Function to parse frames
|
|
102
|
+
def parse_frame(mask: bool, deflate: bool):
|
|
103
|
+
try:
|
|
104
|
+
if mask:
|
|
105
|
+
# Decide whether to include permessage-deflate extension
|
|
106
|
+
extensions = [self.permessage_deflate_masked] if deflate else []
|
|
107
|
+
else:
|
|
108
|
+
extensions = [self.permessage_deflate_unmasked] if deflate else []
|
|
109
|
+
|
|
110
|
+
# Use Frame.parse to parse the frame
|
|
111
|
+
frame_parser = Frame.parse(
|
|
112
|
+
read_exact,
|
|
113
|
+
mask=mask, # Client frames are masked
|
|
114
|
+
max_size=None,
|
|
115
|
+
extensions=extensions
|
|
116
|
+
)
|
|
117
|
+
current_frame = run_coroutine(frame_parser)
|
|
118
|
+
except EOFError as e:
|
|
119
|
+
# Not enough data to parse a complete frame
|
|
120
|
+
raise e
|
|
121
|
+
except (ProtocolError, PayloadTooBig) as e:
|
|
122
|
+
print("Error parsing frame:", e)
|
|
123
|
+
raise e
|
|
124
|
+
except Exception as e:
|
|
125
|
+
print("Error parsing frame:", e)
|
|
126
|
+
raise e
|
|
127
|
+
return current_frame
|
|
128
|
+
|
|
129
|
+
def process_frame(current_frame):
|
|
130
|
+
if current_frame.opcode == Opcode.TEXT:
|
|
131
|
+
message = current_frame.data.decode('utf-8', errors='replace')
|
|
132
|
+
return message
|
|
133
|
+
elif current_frame.opcode == Opcode.BINARY:
|
|
134
|
+
return current_frame.data
|
|
135
|
+
elif current_frame.opcode == Opcode.CLOSE:
|
|
136
|
+
print("Received close frame")
|
|
137
|
+
elif current_frame.opcode == Opcode.PING:
|
|
138
|
+
print("Received ping")
|
|
139
|
+
elif current_frame.opcode == Opcode.PONG:
|
|
140
|
+
print("Received pong")
|
|
141
|
+
else:
|
|
142
|
+
raise WebsocketParseWrongOpcode("Received unknown frame with opcode:", current_frame.opcode)
|
|
143
|
+
|
|
144
|
+
# Create the StreamReader instance
|
|
145
|
+
reader = StreamReader()
|
|
146
|
+
|
|
147
|
+
masked = is_frame_masked(data_bytes)
|
|
148
|
+
deflated = is_frame_deflated(data_bytes)
|
|
149
|
+
|
|
150
|
+
# Feed the data into the reader
|
|
151
|
+
reader.feed_data(data_bytes)
|
|
152
|
+
|
|
153
|
+
# Parse and process frames
|
|
154
|
+
frame = parse_frame(masked, deflated)
|
|
155
|
+
result = process_frame(frame)
|
|
156
|
+
|
|
157
|
+
# This is basically not needed since we restart the 'reader = StreamReader()' each function execution.
|
|
158
|
+
# # After processing, reset the reader's buffer
|
|
159
|
+
# reader.buffer = b''
|
|
160
|
+
|
|
161
|
+
return result
|
|
134
162
|
|
|
135
163
|
|
|
136
164
|
def create_websocket_frame(
|
|
@@ -69,10 +69,10 @@ def install_after_restart(
|
|
|
69
69
|
install_command = install_command + ' --' + install_type
|
|
70
70
|
|
|
71
71
|
if log_level:
|
|
72
|
-
install_command = install_command + ' --
|
|
72
|
+
install_command = install_command + ' --log_level ' + log_level
|
|
73
73
|
|
|
74
74
|
if log_file:
|
|
75
|
-
install_command = install_command + ' --
|
|
75
|
+
install_command = install_command + ' --log_file "' + log_file + '"'
|
|
76
76
|
|
|
77
77
|
# Install the FACT_core repo.
|
|
78
78
|
process.execute_with_live_output(cmd=install_command, verbose=True)
|
|
@@ -6,7 +6,7 @@ from ..loggingw import loggingw
|
|
|
6
6
|
|
|
7
7
|
LOGGER_NAME: str = 'statistics'
|
|
8
8
|
STATISTICS_HEADER: str = (
|
|
9
|
-
'request_time_sent,tls,protocol,host,path,command,status_code,request_size_bytes,response_size_bytes,file_path,'
|
|
9
|
+
'request_time_sent,thread_id,tls,protocol,host,path,command,status_code,request_size_bytes,response_size_bytes,file_path,'
|
|
10
10
|
'process_cmd,error')
|
|
11
11
|
|
|
12
12
|
|
|
@@ -30,6 +30,7 @@ class StatisticsCSVWriter:
|
|
|
30
30
|
|
|
31
31
|
def write_row(
|
|
32
32
|
self,
|
|
33
|
+
thread_id: str,
|
|
33
34
|
host: str,
|
|
34
35
|
tls_type: str,
|
|
35
36
|
tls_version: str,
|
|
@@ -54,6 +55,7 @@ class StatisticsCSVWriter:
|
|
|
54
55
|
|
|
55
56
|
escaped_line_string: str = csvs.escape_csv_line_to_string([
|
|
56
57
|
request_time_sent,
|
|
58
|
+
thread_id,
|
|
57
59
|
tls_info,
|
|
58
60
|
protocol,
|
|
59
61
|
host,
|
|
@@ -73,7 +75,8 @@ class StatisticsCSVWriter:
|
|
|
73
75
|
self,
|
|
74
76
|
error_message: str,
|
|
75
77
|
host: str,
|
|
76
|
-
process_name: str
|
|
78
|
+
process_name: str,
|
|
79
|
+
thread_id: str = str()
|
|
77
80
|
):
|
|
78
81
|
"""
|
|
79
82
|
Write the error message to the statistics CSV file.
|
|
@@ -82,10 +85,12 @@ class StatisticsCSVWriter:
|
|
|
82
85
|
:param error_message: string, error message.
|
|
83
86
|
:param host: string, host, the domain or IP address.
|
|
84
87
|
:param process_name: process name, the command line of the process.
|
|
88
|
+
:param thread_id: integer, the id of the thread.
|
|
85
89
|
:return:
|
|
86
90
|
"""
|
|
87
91
|
|
|
88
92
|
self.write_row(
|
|
93
|
+
thread_id=thread_id,
|
|
89
94
|
host=host,
|
|
90
95
|
tls_type='',
|
|
91
96
|
tls_version='',
|
|
@@ -173,7 +173,7 @@ def get_default_dns_gateway() -> tuple[bool, list[str]]:
|
|
|
173
173
|
|
|
174
174
|
if ' ' in interface_settings['NameServer']:
|
|
175
175
|
interface_settings['NameServer'] = interface_settings['NameServer'].replace(' ', ',')
|
|
176
|
-
if ' ' in interface_settings['DhcpNameServer']:
|
|
176
|
+
if 'DhcpNameServer' in interface_settings and ' ' in interface_settings['DhcpNameServer']:
|
|
177
177
|
interface_settings['DhcpNameServer'] = interface_settings['DhcpNameServer'].replace(' ', ',')
|
|
178
178
|
|
|
179
179
|
if not default_dns_gateway_list:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
atomicshop/__init__.py,sha256=
|
|
1
|
+
atomicshop/__init__.py,sha256=fBF5eT1cIzArZVWu2launQIsr0jJhGhMkV0tYXx0zf4,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
|
|
@@ -45,7 +45,7 @@ atomicshop/urls.py,sha256=aJ0NGS9qqaKeqjkkWBs80jaBBg6MYBiPuLIyPGxscVc,1557
|
|
|
45
45
|
atomicshop/uuids.py,sha256=JSQdm3ZTJiwPQ1gYe6kU0TKS_7suwVrHc8JZDGYlydM,2214
|
|
46
46
|
atomicshop/virtualization.py,sha256=LPP4vjE0Vr10R6DA4lqhfX_WaNdDGRAZUW0Am6VeGco,494
|
|
47
47
|
atomicshop/web.py,sha256=GLdTXgMxg1_0UQaXC4bOvARVyuFg7SPIeJdsCHV8rNE,11662
|
|
48
|
-
atomicshop/websocket_parse.py,sha256=
|
|
48
|
+
atomicshop/websocket_parse.py,sha256=chqR3NSuW5Ndtclzj-46jCuQbyTh6umm1PBKvSkDzXw,16456
|
|
49
49
|
atomicshop/a_installs/ubuntu/docker_rootless.py,sha256=9IPNtGZYjfy1_n6ZRt7gWz9KZgR6XCgevjqq02xk-o0,281
|
|
50
50
|
atomicshop/a_installs/ubuntu/docker_sudo.py,sha256=JzayxeyKDtiuT4Icp2L2LyFRbx4wvpyN_bHLfZ-yX5E,281
|
|
51
51
|
atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py,sha256=yRB-l1zBxdiN6av-FwNkhcBlaeu4zrDPjQ0uPGgpK2I,244
|
|
@@ -76,7 +76,7 @@ atomicshop/addons/process_list/compiled/Win10x64/process_list.exp,sha256=cbvukIT
|
|
|
76
76
|
atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=T2Ncs0MwKlAaCq8UKFMvfQAfcJdDx-nWiHVBfglrLIU,2112
|
|
77
77
|
atomicshop/archiver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
78
|
atomicshop/archiver/_search_in_zip.py,sha256=dd8qFSvIhcKmtnPj_uYNJFPmMwZp4tZys0kKgTw_ACw,8385
|
|
79
|
-
atomicshop/archiver/search_in_archive.py,sha256=
|
|
79
|
+
atomicshop/archiver/search_in_archive.py,sha256=aj35JFNL4Gx1ZBfcnr5MpDhnfOWoOvmnt_ye1KhDQCI,12133
|
|
80
80
|
atomicshop/archiver/sevenz_app_w.py,sha256=BWcJb4f7jZEiETDBKyNLE0f5YLFPQx6B91_ObEIXWf8,3007
|
|
81
81
|
atomicshop/archiver/sevenzs.py,sha256=5i_C50-deC1Cz_GQdMGofV2jeMPbbGWAvih-QnA72cg,1370
|
|
82
82
|
atomicshop/archiver/shutils.py,sha256=BomnK7zT-nQXA1z0i2R2aTv8eu88wPx7tf2HtOdbmEc,1280
|
|
@@ -86,7 +86,7 @@ atomicshop/basics/ansi_escape_codes.py,sha256=WtIkm-BjSZS5J5irDUdAMBNvdX-qXFZcTX
|
|
|
86
86
|
atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
|
|
87
87
|
atomicshop/basics/booleans.py,sha256=V36NaMf8AffhCom_ovQeOZlYcdtGyIcQwWKki6h7O0M,1745
|
|
88
88
|
atomicshop/basics/bytes_arrays.py,sha256=tU7KF0UAFSErGNcan4Uz1GJxeIYVRuUu9sHVZHgyNnw,6542
|
|
89
|
-
atomicshop/basics/classes.py,sha256=
|
|
89
|
+
atomicshop/basics/classes.py,sha256=UayCzPs3eynI3wOzSu-2IJSmTwOB4HwPwgVI2F-7_lQ,12648
|
|
90
90
|
atomicshop/basics/dicts.py,sha256=DeYHIh940pMMBrFhpXt4dsigFVYzTrlqWymNo4Pq_Js,14049
|
|
91
91
|
atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
|
|
92
92
|
atomicshop/basics/enumerations.py,sha256=41VVQYh_vnVapggxKg2IRU5e_EiMpZzX1n1mtxvoSzM,1364
|
|
@@ -126,7 +126,7 @@ atomicshop/file_io/xmls.py,sha256=zh3SuK-dNaFq2NDNhx6ivcf4GYCfGM8M10PcEwDSpxk,21
|
|
|
126
126
|
atomicshop/mitm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
127
127
|
atomicshop/mitm/config_static.py,sha256=ROAtbibSWSsF3BraUbhu-QO3MPIFqYY5KUKgsQbiSkk,7813
|
|
128
128
|
atomicshop/mitm/config_toml_editor.py,sha256=2p1CMcktWRR_NW-SmyDwylu63ad5e0-w1QPMa8ZLDBw,1635
|
|
129
|
-
atomicshop/mitm/connection_thread_worker.py,sha256=
|
|
129
|
+
atomicshop/mitm/connection_thread_worker.py,sha256=J2fYJZS1IuibvvNmeIsm4hkJMxokQFbBk9xwMGjT7YE,19958
|
|
130
130
|
atomicshop/mitm/import_config.py,sha256=0Ij14aISTllTOiWYJpIUMOWobQqGofD6uafui5uWllE,9272
|
|
131
131
|
atomicshop/mitm/initialize_engines.py,sha256=VyJE8QnzlgD3QbX5inz5o6rC3zQ3is9CeTL7-B10g1w,8292
|
|
132
132
|
atomicshop/mitm/message.py,sha256=URR5JKSuAT8XmGIkyprEjlPW2GW4ef_gfUz_GgcFseE,2184
|
|
@@ -139,7 +139,7 @@ atomicshop/mitm/engines/create_module_template.py,sha256=tRjVSm1sD6FzML71Qbuwvit
|
|
|
139
139
|
atomicshop/mitm/engines/create_module_template_example.py,sha256=X5xhvbV6-g9jU_bQVhf_crZmaH50LRWz3bS-faQ18ds,489
|
|
140
140
|
atomicshop/mitm/engines/__parent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
141
141
|
atomicshop/mitm/engines/__parent/parser___parent.py,sha256=RK2wviepP0oeq7zuLpgkvqvTJtc0r0a7hDGWdV0dGc4,657
|
|
142
|
-
atomicshop/mitm/engines/__parent/recorder___parent.py,sha256=
|
|
142
|
+
atomicshop/mitm/engines/__parent/recorder___parent.py,sha256=mHQAvUgsRCy3pJR_ws4v_QBE9Ju5r7RmWhyJyvczyI0,3781
|
|
143
143
|
atomicshop/mitm/engines/__parent/responder___parent.py,sha256=7WQeR3UmMnN74bDwn-0nz2OfhXJ3-ClXpNGUFZ7wJUE,12004
|
|
144
144
|
atomicshop/mitm/engines/__reference_general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
145
145
|
atomicshop/mitm/engines/__reference_general/parser___reference_general.py,sha256=57MEPZMAjTO6xBDZ-yt6lgGJyqRrP0Do5Gk_cgCiPns,2998
|
|
@@ -229,7 +229,7 @@ atomicshop/wrappers/factw/fact_extractor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
|
229
229
|
atomicshop/wrappers/factw/fact_extractor/docker_image.py,sha256=2FyYjnw8gxFNwISQ83OwH-iGivkFi6EAluyCZ0loHEQ,2501
|
|
230
230
|
atomicshop/wrappers/factw/fact_extractor/get_extractor.py,sha256=2mfOAftHIlCcGt1s7MWdq7DsDCuI6wX3MtvcEZ4SK-0,756
|
|
231
231
|
atomicshop/wrappers/factw/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
232
|
-
atomicshop/wrappers/factw/install/install_after_restart.py,sha256=
|
|
232
|
+
atomicshop/wrappers/factw/install/install_after_restart.py,sha256=cr8E9BAEdpJXJ1VdyXr85byU0PvE3tPV0uPALQOmiKg,4318
|
|
233
233
|
atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py,sha256=GFsO9MTH0czKoxkiPJtjalilUwsmFLBCcx9Znv37S4M,5945
|
|
234
234
|
atomicshop/wrappers/factw/postgresql/__init__.py,sha256=xMBn2d3Exo23IPP2F_9-SXmOlhFbwWDgS9KwozSTjA0,162
|
|
235
235
|
atomicshop/wrappers/factw/postgresql/analysis.py,sha256=2Rxzy2jyq3zEKIo53z8VkjuslKE_i5mq2ZpmJAvyd6U,716
|
|
@@ -312,11 +312,11 @@ atomicshop/wrappers/socketw/socket_client.py,sha256=zDX7M3KfUOn0tXR92gZ119HVFgA8
|
|
|
312
312
|
atomicshop/wrappers/socketw/socket_server_tester.py,sha256=Qobmh4XV8ZxLUaw-eW4ESKAbeSLecCKn2OWFzMhadk0,6420
|
|
313
313
|
atomicshop/wrappers/socketw/socket_wrapper.py,sha256=WtylpezgIIBuz-A6PfM0hO1sm9Exd4j3qhDXcFc74-E,35567
|
|
314
314
|
atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0LxIwBA4iVvU,2275
|
|
315
|
-
atomicshop/wrappers/socketw/statistics_csv.py,sha256=
|
|
315
|
+
atomicshop/wrappers/socketw/statistics_csv.py,sha256=SDYI1cN0oaapvPeLxSXiJrelTy6xbZl-bopR0jAjVGE,3149
|
|
316
316
|
atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
317
|
-
atomicshop/wrappers/winregw/winreg_network.py,sha256=
|
|
318
|
-
atomicshop-2.16.
|
|
319
|
-
atomicshop-2.16.
|
|
320
|
-
atomicshop-2.16.
|
|
321
|
-
atomicshop-2.16.
|
|
322
|
-
atomicshop-2.16.
|
|
317
|
+
atomicshop/wrappers/winregw/winreg_network.py,sha256=zZQfps-CdODQaTUADbHAwKHr5RUg7BLafnKWBbKaLN4,8728
|
|
318
|
+
atomicshop-2.16.45.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
|
|
319
|
+
atomicshop-2.16.45.dist-info/METADATA,sha256=E-QukR4G1Pts-0XCHJeKddzx3V2jaC6C5eLog6UU0Kw,10500
|
|
320
|
+
atomicshop-2.16.45.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
321
|
+
atomicshop-2.16.45.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
|
|
322
|
+
atomicshop-2.16.45.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|