monkeytoolbox 0.1.0__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.
@@ -0,0 +1,140 @@
1
+ import logging
2
+ import threading
3
+ from functools import wraps
4
+ from itertools import count
5
+ from threading import Lock, Thread
6
+ from typing import Any, Callable, Iterable, Iterator, Optional, Tuple, TypeVar
7
+
8
+ from monkeytypes import Event
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ def run_worker_threads(
14
+ target: Callable[..., None],
15
+ name_prefix: str,
16
+ args: Tuple = (),
17
+ num_workers: int = 2,
18
+ ):
19
+ worker_threads = []
20
+ counter = run_worker_threads.counters.setdefault(name_prefix, count(start=1))
21
+ for i in range(0, num_workers):
22
+ name = f"{name_prefix}-{next(counter):02d}"
23
+ t = create_daemon_thread(target=target, name=name, args=args)
24
+ t.start()
25
+ worker_threads.append(t)
26
+
27
+ for t in worker_threads:
28
+ t.join()
29
+
30
+
31
+ run_worker_threads.counters = {}
32
+
33
+
34
+ def create_daemon_thread(target: Callable[..., None], name: str, args: Tuple = ()) -> Thread:
35
+ return Thread(target=target, name=name, args=args, daemon=True)
36
+
37
+
38
+ def interruptible_iter(
39
+ iterator: Iterable,
40
+ interrupt: Event,
41
+ log_message: Optional[str] = None,
42
+ log_level: int = logging.DEBUG,
43
+ ) -> Any:
44
+ """
45
+ Wraps an iterator so that the iterator can be interrupted if the `interrupt` event is set. This
46
+ is a convinient way to make loops interruptible and avoids the need to add an `if` to each and
47
+ every loop.
48
+ :param Iterable iterator: An iterator that will be made interruptible.
49
+ :param Event interrupt: An `Event` that, if set, will prevent the remainder of the
50
+ iterator's items from being processed.
51
+ :param str log_message: A message to be logged if the iterator is interrupted. If `log_message`
52
+ is `None` (default), then no message is logged.
53
+ :param int log_level: The log level at which to log `log_message`, defaults to `logging.DEBUG`.
54
+ """
55
+ for i in iterator:
56
+ if interrupt.is_set():
57
+ if log_message:
58
+ logger.log(log_level, log_message)
59
+
60
+ break
61
+
62
+ yield i
63
+
64
+
65
+ def interruptible_function(*, msg: Optional[str] = None, default_return_value: Any = None):
66
+ """
67
+ This decorator allows a function to be skipped if an interrupt (`Event`) is set. This is
68
+ useful for interrupting running code without introducing duplicate `if` checks at the beginning
69
+ of each function.
70
+
71
+ Note: It is required that the decorated function accept a keyword-only argument named
72
+ "interrupt".
73
+
74
+ Example:
75
+ def run_algorithm(*inputs, interrupt: Event):
76
+ return_value = do_action_1(inputs[1], interrupt=interrupt)
77
+ return_value = do_action_2(return_value + inputs[2], interrupt=interrupt)
78
+ return_value = do_action_3(return_value + inputs[3], interrupt=interrupt)
79
+
80
+ return return_value
81
+
82
+ @interruptible_function(msg="Interrupt detected, skipping action 1", default_return_value=0)
83
+ def do_action_1(input, *, interrupt: Event):
84
+ # Process input
85
+ ...
86
+
87
+ @interruptible_function(msg="Interrupt detected, skipping action 2", default_return_value=0)
88
+ def do_action_2(input, *, interrupt: Event):
89
+ # Process input
90
+ ...
91
+
92
+ @interruptible_function(msg="Interrupt detected, skipping action 2", default_return_value=0)
93
+ def do_action_2(input, *, interrupt: Event):
94
+ # Process input
95
+ ...
96
+
97
+ :param str msg: A message to log at the debug level if an interrupt is detected. Defaults to
98
+ None.
99
+ :param Any default_return_value: A value to return if the wrapped function is not run. Defaults
100
+ to None.
101
+ """
102
+
103
+ def _decorator(fn):
104
+ @wraps(fn)
105
+ def _wrapper(*args, interrupt: Event, **kwargs):
106
+ if interrupt.is_set():
107
+ if msg:
108
+ logger.debug(msg)
109
+ return default_return_value
110
+
111
+ return fn(*args, interrupt=interrupt, **kwargs)
112
+
113
+ return _wrapper
114
+
115
+ return _decorator
116
+
117
+
118
+ class InterruptableThreadMixin:
119
+ def __init__(self):
120
+ self._interrupted = threading.Event()
121
+
122
+ def stop(self):
123
+ """Stop a running thread."""
124
+ self._interrupted.set()
125
+
126
+
127
+ T = TypeVar("T")
128
+
129
+
130
+ class ThreadSafeIterator(Iterator[T]):
131
+ """Provides a thread-safe iterator that wraps another iterator"""
132
+
133
+ def __init__(self, iterator: Iterator[T]):
134
+ self._lock = Lock()
135
+ self._iterator = iterator
136
+
137
+ def __next__(self) -> T:
138
+ while True:
139
+ with self._lock:
140
+ return next(self._iterator)
@@ -0,0 +1,52 @@
1
+ from typing import Any, Mapping
2
+
3
+ import ntsecuritycon
4
+ import win32api
5
+ import win32security
6
+
7
+ ACCESS_MODE_GRANT_ACCESS = win32security.GRANT_ACCESS
8
+ ACCESS_PERMISSIONS_FULL_CONTROL = ntsecuritycon.FILE_ALL_ACCESS
9
+ INHERITANCE_OBJECT_AND_CONTAINER = (
10
+ win32security.CONTAINER_INHERIT_ACE | win32security.OBJECT_INHERIT_ACE
11
+ )
12
+
13
+
14
+ def get_security_descriptor_for_owner_only_permissions():
15
+ user_sid = _get_user_pySID_object()
16
+ entries = [_get_ace_for_owner_only_permissions(user_sid)]
17
+
18
+ dacl = win32security.ACL()
19
+ dacl.SetEntriesInAcl(entries)
20
+
21
+ security_descriptor = win32security.SECURITY_DESCRIPTOR()
22
+ security_descriptor.SetSecurityDescriptorControl(
23
+ win32security.SE_DACL_PROTECTED, win32security.SE_DACL_PROTECTED
24
+ )
25
+ security_descriptor.SetSecurityDescriptorDacl(1, dacl, 0)
26
+
27
+ return security_descriptor
28
+
29
+
30
+ def _get_user_pySID_object():
31
+ # Note: We do this using the process handle and token instead of by account name, as some SIDs
32
+ # have no corresponding account name, such as a logon SID that identifies a logon session. This
33
+ # is particularly an issue when using the SMB exploiter. See issue #3173.
34
+ process_handle = win32api.GetCurrentProcess()
35
+ token_handle = win32security.OpenProcessToken(process_handle, win32security.TOKEN_ALL_ACCESS)
36
+
37
+ sid, _ = win32security.GetTokenInformation(token_handle, win32security.TokenUser)
38
+
39
+ return sid
40
+
41
+
42
+ def _get_ace_for_owner_only_permissions(user_sid) -> Mapping[str, Any]:
43
+ return {
44
+ "AccessMode": ACCESS_MODE_GRANT_ACCESS,
45
+ "AccessPermissions": ACCESS_PERMISSIONS_FULL_CONTROL,
46
+ "Inheritance": INHERITANCE_OBJECT_AND_CONTAINER,
47
+ "Trustee": {
48
+ "TrusteeType": win32security.TRUSTEE_IS_USER,
49
+ "TrusteeForm": win32security.TRUSTEE_IS_SID,
50
+ "Identifier": user_sid,
51
+ },
52
+ }