atomicshop 2.21.1__py3-none-any.whl → 3.0.1__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/basics/multiprocesses.py +228 -30
- atomicshop/dns.py +2 -0
- atomicshop/mitm/config_static.py +2 -1
- atomicshop/mitm/engines/create_module_template.py +2 -7
- atomicshop/mitm/import_config.py +36 -44
- atomicshop/mitm/initialize_engines.py +9 -24
- atomicshop/mitm/mitm_main.py +187 -59
- atomicshop/networks.py +448 -0
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/dockerw/dockerw.py +17 -21
- atomicshop/wrappers/mongodbw/mongodbw.py +1 -0
- atomicshop/wrappers/psutilw/{networks.py → psutil_networks.py} +3 -1
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +76 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +262 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +51 -82
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +235 -0
- atomicshop/wrappers/socketw/accepter.py +15 -1
- atomicshop/wrappers/socketw/creator.py +7 -1
- atomicshop/wrappers/socketw/dns_server.py +33 -39
- atomicshop/wrappers/socketw/exception_wrapper.py +20 -11
- atomicshop/wrappers/socketw/socket_wrapper.py +29 -78
- atomicshop/wrappers/winregw/winreg_network.py +20 -0
- {atomicshop-2.21.1.dist-info → atomicshop-3.0.1.dist-info}/METADATA +2 -1
- {atomicshop-2.21.1.dist-info → atomicshop-3.0.1.dist-info}/RECORD +28 -24
- atomicshop/wrappers/pywin32w/wmis/helpers.py +0 -131
- {atomicshop-2.21.1.dist-info → atomicshop-3.0.1.dist-info}/LICENSE.txt +0 -0
- {atomicshop-2.21.1.dist-info → atomicshop-3.0.1.dist-info}/WHEEL +0 -0
- {atomicshop-2.21.1.dist-info → atomicshop-3.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from ctypes import wintypes
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Constants and functions for creating root-enumerated device nodes
|
|
7
|
+
# ---------------------------------------------------------------------------
|
|
8
|
+
# Compatibility shim: wintypes.ULONG_PTR is missing on some builds
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
if not hasattr(wintypes, "ULONG_PTR"):
|
|
11
|
+
if ctypes.sizeof(ctypes.c_void_p) == 8: # 64-bit Python
|
|
12
|
+
wintypes.ULONG_PTR = ctypes.c_uint64
|
|
13
|
+
else: # 32-bit Python
|
|
14
|
+
wintypes.ULONG_PTR = ctypes.c_uint32
|
|
15
|
+
|
|
16
|
+
# ------------------------------------------------------------------
|
|
17
|
+
# SetupDi* “device‑registry property” indices (SPDRP_…)
|
|
18
|
+
# From Microsoft’s setupapi.h – keep them as ints.
|
|
19
|
+
# ------------------------------------------------------------------
|
|
20
|
+
SPDRP_DEVICEDESC = 0 # REG_SZ – Device description (friendly name)
|
|
21
|
+
SPDRP_HARDWAREID = 1 # REG_MULTI_SZ – Hardware‑ID list
|
|
22
|
+
SPDRP_COMPATIBLEIDS = 2 # REG_MULTI_SZ – Compatible‑ID list
|
|
23
|
+
SPDRP_SERVICE = 4 # REG_SZ – Service/miniport to load
|
|
24
|
+
SPDRP_CLASS = 7 # REG_SZ – Class name (e.g. "Net")
|
|
25
|
+
SPDRP_CLASSGUID = 8 # REG_SZ – Class GUID in string form
|
|
26
|
+
|
|
27
|
+
# newdev.h (Windows SDK) / SetupAPI
|
|
28
|
+
DIF_REGISTERDEVICE = 0x00000019
|
|
29
|
+
DIF_REMOVE = 0x00000005
|
|
30
|
+
DICD_GENERATE_ID = 0x00000001
|
|
31
|
+
INSTALLFLAG_FORCE = 0x00000001 # install even if “better” driver exists
|
|
32
|
+
INSTALLFLAG_READONLY = 0x00000002 # don’t write driver to driver store
|
|
33
|
+
INSTALLFLAG_NONINTERACTIVE = 0x00000004 # never display UI (silent mode)
|
|
34
|
+
|
|
35
|
+
DIGCF_PRESENT = 0x00000002
|
|
36
|
+
ERROR_NO_MORE_ITEMS = 259
|
|
37
|
+
|
|
38
|
+
setupapi = ctypes.WinDLL("setupapi", use_last_error=True)
|
|
39
|
+
newdev = ctypes.WinDLL("newdev", use_last_error=True)
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Structures & prototypes
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
class SP_DEVINFO_DATA(ctypes.Structure):
|
|
45
|
+
_fields_ = [
|
|
46
|
+
("cbSize", wintypes.DWORD),
|
|
47
|
+
("ClassGuid", ctypes.c_byte * 16),
|
|
48
|
+
("DevInst", wintypes.DWORD),
|
|
49
|
+
("Reserved", wintypes.ULONG_PTR),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# --- creation helpers ------------------------------------------------------
|
|
53
|
+
SetupDiCreateDeviceInfoList = setupapi.SetupDiCreateDeviceInfoList
|
|
54
|
+
SetupDiCreateDeviceInfoList.argtypes = [ctypes.POINTER(ctypes.c_byte * 16), wintypes.HWND]
|
|
55
|
+
SetupDiCreateDeviceInfoList.restype = wintypes.HANDLE
|
|
56
|
+
|
|
57
|
+
SetupDiCreateDeviceInfoW = setupapi.SetupDiCreateDeviceInfoW
|
|
58
|
+
SetupDiCreateDeviceInfoW.argtypes = [
|
|
59
|
+
wintypes.HANDLE, wintypes.LPCWSTR,
|
|
60
|
+
ctypes.POINTER(ctypes.c_byte * 16),
|
|
61
|
+
wintypes.LPCWSTR, wintypes.HWND, wintypes.DWORD,
|
|
62
|
+
ctypes.POINTER(SP_DEVINFO_DATA)
|
|
63
|
+
]
|
|
64
|
+
SetupDiCreateDeviceInfoW.restype = wintypes.BOOL
|
|
65
|
+
|
|
66
|
+
SetupDiSetDeviceRegistryPropertyW = setupapi.SetupDiSetDeviceRegistryPropertyW
|
|
67
|
+
SetupDiSetDeviceRegistryPropertyW.argtypes = [
|
|
68
|
+
wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA), wintypes.DWORD,
|
|
69
|
+
wintypes.LPBYTE, wintypes.DWORD
|
|
70
|
+
]
|
|
71
|
+
SetupDiSetDeviceRegistryPropertyW.restype = wintypes.BOOL
|
|
72
|
+
|
|
73
|
+
SetupDiCallClassInstaller = setupapi.SetupDiCallClassInstaller
|
|
74
|
+
SetupDiCallClassInstaller.argtypes = [
|
|
75
|
+
wintypes.DWORD, wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA)
|
|
76
|
+
]
|
|
77
|
+
SetupDiCallClassInstaller.restype = wintypes.BOOL
|
|
78
|
+
|
|
79
|
+
# --- enumeration / removal -------------------------------------------------
|
|
80
|
+
SetupDiGetClassDevsW = setupapi.SetupDiGetClassDevsW
|
|
81
|
+
SetupDiGetClassDevsW.argtypes = [ctypes.POINTER(ctypes.c_byte * 16),
|
|
82
|
+
wintypes.LPCWSTR, wintypes.HWND, wintypes.DWORD]
|
|
83
|
+
SetupDiGetClassDevsW.restype = wintypes.HANDLE
|
|
84
|
+
|
|
85
|
+
SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
|
|
86
|
+
SetupDiEnumDeviceInfo.argtypes = [wintypes.HANDLE, wintypes.DWORD,
|
|
87
|
+
ctypes.POINTER(SP_DEVINFO_DATA)]
|
|
88
|
+
SetupDiEnumDeviceInfo.restype = wintypes.BOOL
|
|
89
|
+
|
|
90
|
+
SetupDiGetDeviceRegistryPropertyW = setupapi.SetupDiGetDeviceRegistryPropertyW
|
|
91
|
+
SetupDiGetDeviceRegistryPropertyW.argtypes = [
|
|
92
|
+
wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA), wintypes.DWORD,
|
|
93
|
+
ctypes.POINTER(wintypes.DWORD), wintypes.PBYTE, wintypes.DWORD,
|
|
94
|
+
ctypes.POINTER(wintypes.DWORD)
|
|
95
|
+
]
|
|
96
|
+
SetupDiGetDeviceRegistryPropertyW.restype = wintypes.BOOL
|
|
97
|
+
|
|
98
|
+
SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
|
|
99
|
+
SetupDiDestroyDeviceInfoList.argtypes = [wintypes.HANDLE]
|
|
100
|
+
SetupDiDestroyDeviceInfoList.restype = wintypes.BOOL
|
|
101
|
+
|
|
102
|
+
UpdateDriverForPlugAndPlayDevicesW = newdev.UpdateDriverForPlugAndPlayDevicesW
|
|
103
|
+
UpdateDriverForPlugAndPlayDevicesW.argtypes = [
|
|
104
|
+
wintypes.HWND, wintypes.LPCWSTR, wintypes.LPCWSTR,
|
|
105
|
+
wintypes.DWORD, ctypes.POINTER(wintypes.BOOL)
|
|
106
|
+
]
|
|
107
|
+
UpdateDriverForPlugAndPlayDevicesW.restype = wintypes.BOOL
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# 1. Create a root-enumerated devnode (idempotent)
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
def create_root_enumerated_devnode(
|
|
114
|
+
class_guid: str,
|
|
115
|
+
friendly_name: str, # what shows in Device Manager
|
|
116
|
+
hardware_ids: "list[str] | str",
|
|
117
|
+
compatible_ids: "list[str] | str | None" = None,
|
|
118
|
+
devdesc_override: str | None = None,
|
|
119
|
+
create_flags: int = DICD_GENERATE_ID,
|
|
120
|
+
existing_ok: bool = True,
|
|
121
|
+
) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Programmatically create a *root‑enumerated* device node, set its
|
|
124
|
+
Hardware‑ID (and optional Compatible‑ID) list, then ask Plug and Play
|
|
125
|
+
to register/ install whatever driver matches those IDs.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
class_guid : string representation of a GUID.
|
|
130
|
+
Device‑class GUID (e.g. GUID_DEVCLASS_NET, GUID_DEVCLASS_MEDIA …).
|
|
131
|
+
Example:
|
|
132
|
+
class_guid="{4d36e972-e325-11ce-bfc1-08002be10318}"
|
|
133
|
+
This is the GUID for network adapters.
|
|
134
|
+
|
|
135
|
+
friendly_name : str
|
|
136
|
+
Initial instance name placed in the registry (DeviceDesc).
|
|
137
|
+
Also, will be shown in Device Manager.
|
|
138
|
+
|
|
139
|
+
hardware_ids : str | list[str]
|
|
140
|
+
One or more hardware IDs (MULTI_SZ). The *first* one is the key
|
|
141
|
+
identifier PnP uses when selecting an INF.
|
|
142
|
+
|
|
143
|
+
compatible_ids : str | list[str] | None
|
|
144
|
+
Optional Compatible‑ID list (another MULTI_SZ, lower priority).
|
|
145
|
+
|
|
146
|
+
devdesc_override : str | None
|
|
147
|
+
If supplied, written to SPDRP_DEVICEDESC (rarely necessary because
|
|
148
|
+
the INF’s own DeviceDesc usually replaces it).
|
|
149
|
+
|
|
150
|
+
create_flags : int
|
|
151
|
+
Flags for SetupDiCreateDeviceInfoW. Default is DICD_GENERATE_ID.
|
|
152
|
+
|
|
153
|
+
existing_ok : bool
|
|
154
|
+
If True, silently succeed when the devnode already exists.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
class_guid_bytes = uuid.UUID(class_guid).bytes_le
|
|
158
|
+
class_guid_object = (ctypes.c_byte * 16).from_buffer_copy(class_guid_bytes)
|
|
159
|
+
|
|
160
|
+
# --- 1. Create a temporary empty device‑info set -------------------
|
|
161
|
+
hdi = SetupDiCreateDeviceInfoList(class_guid_object, None) # Open a new, empty set
|
|
162
|
+
if hdi == wintypes.HANDLE(-1).value: # INVALID_HANDLE_VALUE?
|
|
163
|
+
raise ctypes.WinError(ctypes.get_last_error()) # Bail out on failure
|
|
164
|
+
|
|
165
|
+
# Prepare the SP_DEVINFO_DATA structure -----------------------------
|
|
166
|
+
devinfo = SP_DEVINFO_DATA() # Zero‑initialised struct
|
|
167
|
+
devinfo.cbSize = ctypes.sizeof(devinfo) # Must set cbSize field
|
|
168
|
+
|
|
169
|
+
# --- 2. Create (or open) the devnode itself ------------------------
|
|
170
|
+
if not SetupDiCreateDeviceInfoW(
|
|
171
|
+
hdi, # Info‑set handle
|
|
172
|
+
friendly_name, # Instance name
|
|
173
|
+
class_guid_object, # Class GUID
|
|
174
|
+
None, None, # (Description, parent window)
|
|
175
|
+
create_flags, # e.g. DICD_GENERATE_ID
|
|
176
|
+
ctypes.byref(devinfo) # Receives devinfo data
|
|
177
|
+
):
|
|
178
|
+
err = ctypes.get_last_error() # Capture error now
|
|
179
|
+
SetupDiDestroyDeviceInfoList(hdi) # Clean up handle
|
|
180
|
+
if not (existing_ok and err == 0xE0000217): # ERROR_DEVINST_ALREADY_EXISTS
|
|
181
|
+
raise ctypes.WinError(err) # Re‑raise unless allowed
|
|
182
|
+
|
|
183
|
+
# --- 3. Build MULTI_SZ buffers and write registry properties -------
|
|
184
|
+
def _multisz(lst_or_str): # Helper → MULTI_SZ buffer
|
|
185
|
+
buf = lst_or_str if isinstance(lst_or_str, str) else "\0".join(lst_or_str)
|
|
186
|
+
return ctypes.create_unicode_buffer(buf + "\0") # Extra trailing NUL
|
|
187
|
+
|
|
188
|
+
# Hardware‑ID list (required) ---------------------------
|
|
189
|
+
hwid = _multisz(hardware_ids) # Build MULTI_SZ buffer
|
|
190
|
+
if not SetupDiSetDeviceRegistryPropertyW(
|
|
191
|
+
hdi, ctypes.byref(devinfo), SPDRP_HARDWAREID, # Property to set
|
|
192
|
+
ctypes.cast(hwid, wintypes.LPBYTE), # Cast to LPBYTE
|
|
193
|
+
(len(hwid) + 1) * ctypes.sizeof(ctypes.c_wchar) # Size in bytes
|
|
194
|
+
):
|
|
195
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
196
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
197
|
+
|
|
198
|
+
# Compatible‑ID list (optional) -------------------------
|
|
199
|
+
if compatible_ids:
|
|
200
|
+
cid = _multisz(compatible_ids) # Build MULTI_SZ buffer
|
|
201
|
+
if not SetupDiSetDeviceRegistryPropertyW(
|
|
202
|
+
hdi, ctypes.byref(devinfo), SPDRP_COMPATIBLEIDS,
|
|
203
|
+
ctypes.cast(cid, wintypes.LPBYTE),
|
|
204
|
+
(len(cid) + 1) * ctypes.sizeof(ctypes.c_wchar)
|
|
205
|
+
):
|
|
206
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
207
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
208
|
+
|
|
209
|
+
# DeviceDesc override (optional) -----------------------
|
|
210
|
+
if devdesc_override:
|
|
211
|
+
desc = ctypes.create_unicode_buffer(devdesc_override + "\0")
|
|
212
|
+
if not SetupDiSetDeviceRegistryPropertyW(
|
|
213
|
+
hdi, ctypes.byref(devinfo), SPDRP_DEVICEDESC,
|
|
214
|
+
ctypes.cast(desc, wintypes.LPBYTE),
|
|
215
|
+
(len(desc) + 1) * ctypes.sizeof(ctypes.c_wchar)
|
|
216
|
+
):
|
|
217
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
218
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
219
|
+
|
|
220
|
+
# --- 4. Hand the devnode to the class installer --------------------
|
|
221
|
+
if not SetupDiCallClassInstaller(
|
|
222
|
+
DIF_REGISTERDEVICE, # “Install this device”
|
|
223
|
+
hdi, ctypes.byref(devinfo)
|
|
224
|
+
):
|
|
225
|
+
err = ctypes.get_last_error()
|
|
226
|
+
# ERROR_DI_DO_DEFAULT means “already registered / nothing to do”
|
|
227
|
+
if not (existing_ok and err == 0xE000020E):
|
|
228
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
229
|
+
raise ctypes.WinError(err)
|
|
230
|
+
|
|
231
|
+
# --- 5. Final cleanup ----------------------------------------------
|
|
232
|
+
SetupDiDestroyDeviceInfoList(hdi) # Always release handle
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ---------------------------------------------------------------------------
|
|
236
|
+
# 2. Bind driver from netloop.inf (idempotent)
|
|
237
|
+
# ---------------------------------------------------------------------------
|
|
238
|
+
def update_driver_for_hwids(
|
|
239
|
+
hardware_ids: "str | list[str]",
|
|
240
|
+
inf_path: str,
|
|
241
|
+
force_install: bool = False,
|
|
242
|
+
quiet: bool = True,
|
|
243
|
+
existing_ok: bool = True,
|
|
244
|
+
) -> bool:
|
|
245
|
+
"""
|
|
246
|
+
Install / update the driver in *inf_path* for every present device whose first
|
|
247
|
+
Hardware‑ID matches *hardware_ids*.
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
hardware_ids : str | list[str]
|
|
252
|
+
Single Hardware‑ID string or a list of IDs. Each is passed separately to
|
|
253
|
+
UpdateDriverForPlugAndPlayDevicesW.
|
|
254
|
+
|
|
255
|
+
inf_path : str
|
|
256
|
+
Full path to the target driver’s .INF file (must already be accessible or
|
|
257
|
+
pre‑staged).
|
|
258
|
+
|
|
259
|
+
force_install : bool, default False
|
|
260
|
+
If True the function sets INSTALLFLAG_FORCE so the specified INF will be
|
|
261
|
+
applied even when Windows thinks a “better” driver is already installed.
|
|
262
|
+
|
|
263
|
+
quiet : bool, default True
|
|
264
|
+
If True the installation runs without UI (parent window = NULL). Set
|
|
265
|
+
False if you want progress dialogs.
|
|
266
|
+
|
|
267
|
+
existing_ok : bool, default True
|
|
268
|
+
When *False*, a return code of ERROR_NO_MORE_ITEMS (no devices found) or
|
|
269
|
+
ERROR_DI_DO_DEFAULT (“already using this driver”) is treated as an error.
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
bool
|
|
274
|
+
True → Windows signalled that a reboot is required.
|
|
275
|
+
False → No reboot required.
|
|
276
|
+
|
|
277
|
+
=====================================================================
|
|
278
|
+
|
|
279
|
+
Examples
|
|
280
|
+
--------
|
|
281
|
+
1. Force‑install the Microsoft KM‑Test Loopback driver that ships with
|
|
282
|
+
Windows (the same scenario as the original hard‑coded function)::
|
|
283
|
+
|
|
284
|
+
reboot_needed = update_driver_for_hwids(
|
|
285
|
+
hardware_ids="*ROOT\\NET\\0000",
|
|
286
|
+
inf_path=r"C:\\Windows\\INF\\netloop.inf",
|
|
287
|
+
force_install=True
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
2. Install an Intel i219‑V NIC driver only if Windows agrees it is the best
|
|
291
|
+
match (no force flag) for either of two possible PCI IDs::
|
|
292
|
+
|
|
293
|
+
reboot_needed = update_driver_for_hwids(
|
|
294
|
+
hardware_ids=[
|
|
295
|
+
"PCI\\VEN_8086&DEV_15B8",
|
|
296
|
+
"PCI\\VEN_8086&DEV_15BB"
|
|
297
|
+
],
|
|
298
|
+
inf_path=r"D:\\Drivers\\PRO1000\\e1r.inf"
|
|
299
|
+
)
|
|
300
|
+
"""
|
|
301
|
+
# Normalise to a Python list for uniform processing
|
|
302
|
+
ids: list[str] = [hardware_ids] if isinstance(hardware_ids, str) else list(hardware_ids)
|
|
303
|
+
|
|
304
|
+
# Build the flag word sent to UpdateDriverForPlugAndPlayDevicesW
|
|
305
|
+
flags = INSTALLFLAG_FORCE if force_install else 0
|
|
306
|
+
if quiet:
|
|
307
|
+
flags |= INSTALLFLAG_NONINTERACTIVE
|
|
308
|
+
|
|
309
|
+
any_reboot = False # track whether *any* call needs reboot
|
|
310
|
+
for hid in ids:
|
|
311
|
+
reboot = wintypes.BOOL(False)
|
|
312
|
+
ok = UpdateDriverForPlugAndPlayDevicesW(
|
|
313
|
+
None, # hwndParent → silent (we add INSTALLFLAG_NONINTERACTIVE)
|
|
314
|
+
hid, # first Hardware‑ID to match
|
|
315
|
+
inf_path, # target driver INF
|
|
316
|
+
flags, # INSTALLFLAG_* bitmask
|
|
317
|
+
ctypes.byref(reboot) # tells us if reboot required
|
|
318
|
+
)
|
|
319
|
+
if not ok:
|
|
320
|
+
err = ctypes.get_last_error()
|
|
321
|
+
# ERROR_NO_MORE_ITEMS (0xE000020B): no matching devices present
|
|
322
|
+
# ERROR_DI_DO_DEFAULT (0xE000020E): already using this driver
|
|
323
|
+
benign = {0xE000020B, 0xE000020E}
|
|
324
|
+
if not (existing_ok and err in benign):
|
|
325
|
+
raise ctypes.WinError(err)
|
|
326
|
+
any_reboot |= bool(reboot.value)
|
|
327
|
+
|
|
328
|
+
return any_reboot
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def add_device(
|
|
332
|
+
class_guid: str,
|
|
333
|
+
friendly_name: str,
|
|
334
|
+
hardware_ids: "list[str] | str",
|
|
335
|
+
inf_path: str,
|
|
336
|
+
compatible_ids: "list[str] | str | None" = None,
|
|
337
|
+
devdesc_override: str | None = None,
|
|
338
|
+
create_flags: int = DICD_GENERATE_ID,
|
|
339
|
+
existing_ok: bool = True,
|
|
340
|
+
force_install: bool = False,
|
|
341
|
+
quiet: bool = True
|
|
342
|
+
) -> None:
|
|
343
|
+
"""
|
|
344
|
+
Create a root-enumerated device node and bind a driver to it.
|
|
345
|
+
This is a wrapper around the two functions create_root_enumerated_devnode() and
|
|
346
|
+
update_driver_for_hwids().
|
|
347
|
+
|
|
348
|
+
This adds the device to the system and binds the driver to it.
|
|
349
|
+
|
|
350
|
+
:param class_guid: string representation of a GUID.
|
|
351
|
+
Device class GUID (e.g. GUID_DEVCLASS_NET, GUID_DEVCLASS_MEDIA …).
|
|
352
|
+
Example:
|
|
353
|
+
class_guid="{4d36e972-e325-11ce-bfc1-08002be10318}"
|
|
354
|
+
This is the GUID for network adapters.
|
|
355
|
+
:param friendly_name: str
|
|
356
|
+
Initial instance name placed in the registry (DeviceDesc).
|
|
357
|
+
Also, will be shown in Device Manager.
|
|
358
|
+
:param hardware_ids: str | list[str]
|
|
359
|
+
One or more hardware IDs (MULTI_SZ). The *first* one is the key
|
|
360
|
+
identifier PnP uses when selecting an INF.
|
|
361
|
+
:param inf_path: str
|
|
362
|
+
Full path to the target driver’s .INF file (must already be accessible or
|
|
363
|
+
pre-staged).
|
|
364
|
+
:param compatible_ids: str | list[str] | None
|
|
365
|
+
Optional Compatible-ID list (another MULTI_SZ, lower priority).
|
|
366
|
+
:param devdesc_override: str | None
|
|
367
|
+
If supplied, written to SPDRP_DEVICEDESC (rarely necessary because
|
|
368
|
+
the INF’s own DeviceDesc usually replaces it).
|
|
369
|
+
:param create_flags: int
|
|
370
|
+
Flags for SetupDiCreateDeviceInfoW. Default is DICD_GENERATE_ID.
|
|
371
|
+
:param existing_ok: bool
|
|
372
|
+
If True, silently succeed when the devnode already exists.
|
|
373
|
+
:param force_install: bool, default False
|
|
374
|
+
If True the function sets INSTALLFLAG_FORCE so the specified INF will be
|
|
375
|
+
applied even when Windows thinks a “better” driver is already installed.
|
|
376
|
+
:param quiet: bool, default True
|
|
377
|
+
If True the installation runs without UI (parent window = NULL). Set
|
|
378
|
+
False if you want progress dialogs.
|
|
379
|
+
:return: None
|
|
380
|
+
|
|
381
|
+
=====================================================================
|
|
382
|
+
Examples
|
|
383
|
+
--------
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
"""
|
|
388
|
+
create_root_enumerated_devnode(
|
|
389
|
+
class_guid=class_guid,
|
|
390
|
+
friendly_name=friendly_name,
|
|
391
|
+
hardware_ids=hardware_ids,
|
|
392
|
+
compatible_ids=compatible_ids,
|
|
393
|
+
devdesc_override=devdesc_override,
|
|
394
|
+
create_flags=create_flags,
|
|
395
|
+
existing_ok=existing_ok
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
update_driver_for_hwids(
|
|
399
|
+
hardware_ids=hardware_ids,
|
|
400
|
+
inf_path=inf_path,
|
|
401
|
+
force_install=force_install,
|
|
402
|
+
quiet=quiet,
|
|
403
|
+
existing_ok=existing_ok
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def remove_device(
|
|
408
|
+
pnp_device_id: str,
|
|
409
|
+
class_guid: str
|
|
410
|
+
) -> bool:
|
|
411
|
+
"""
|
|
412
|
+
Delete the single device whose PNPDeviceID
|
|
413
|
+
equals the string you pass in (case-insensitive). Returns True on
|
|
414
|
+
success, False if no matching devnode was found.
|
|
415
|
+
|
|
416
|
+
:param pnp_device_id: PNPDeviceID of the device to remove.
|
|
417
|
+
If you're using the Win32_NetworkAdapter class, you can
|
|
418
|
+
get the PNPDeviceID from the object itself: network_config.PNPDeviceID
|
|
419
|
+
:param class_guid: string representation of the device class GUID.
|
|
420
|
+
Example: '{4d36e972-e325-11ce-bfc1-08002be10318}' for network adapters.
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
class_guid_bytes = uuid.UUID(class_guid).bytes_le
|
|
424
|
+
class_guid_object = (ctypes.c_byte * 16).from_buffer_copy(class_guid_bytes)
|
|
425
|
+
|
|
426
|
+
# Get only PRESENT devices in the Network class
|
|
427
|
+
hdi = SetupDiGetClassDevsW(class_guid_object, None, None, DIGCF_PRESENT)
|
|
428
|
+
if hdi == wintypes.HANDLE(-1).value:
|
|
429
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
430
|
+
|
|
431
|
+
devinfo = SP_DEVINFO_DATA()
|
|
432
|
+
devinfo.cbSize = ctypes.sizeof(devinfo)
|
|
433
|
+
index = 0
|
|
434
|
+
removed = False
|
|
435
|
+
|
|
436
|
+
# Helper to fetch the instance-ID of the current element
|
|
437
|
+
instance_buf_len = 512
|
|
438
|
+
GetInstanceId = setupapi.SetupDiGetDeviceInstanceIdW
|
|
439
|
+
GetInstanceId.argtypes = [
|
|
440
|
+
wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA),
|
|
441
|
+
wintypes.LPWSTR, wintypes.DWORD, ctypes.POINTER(wintypes.DWORD)
|
|
442
|
+
]
|
|
443
|
+
GetInstanceId.restype = wintypes.BOOL
|
|
444
|
+
inst_buf = ctypes.create_unicode_buffer(instance_buf_len)
|
|
445
|
+
|
|
446
|
+
while SetupDiEnumDeviceInfo(hdi, index, ctypes.byref(devinfo)):
|
|
447
|
+
index += 1
|
|
448
|
+
|
|
449
|
+
if not GetInstanceId(hdi, ctypes.byref(devinfo),
|
|
450
|
+
inst_buf, instance_buf_len, None):
|
|
451
|
+
continue
|
|
452
|
+
|
|
453
|
+
if inst_buf.value.lower() != pnp_device_id.lower():
|
|
454
|
+
continue # not the target
|
|
455
|
+
|
|
456
|
+
# Found it → remove
|
|
457
|
+
if not SetupDiCallClassInstaller(DIF_REMOVE, hdi, ctypes.byref(devinfo)):
|
|
458
|
+
err = ctypes.get_last_error()
|
|
459
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
460
|
+
raise ctypes.WinError(err)
|
|
461
|
+
|
|
462
|
+
removed = True
|
|
463
|
+
break
|
|
464
|
+
|
|
465
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
466
|
+
return removed
|
|
@@ -172,6 +172,16 @@ def add_execution_permissions_for_file(image_id_or_name: str, file_path: str, pr
|
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
def stop_remove_containers_by_image_name(image_name: str):
|
|
175
|
+
def stop_remove_container(container: Container):
|
|
176
|
+
"""
|
|
177
|
+
Stop and remove a container.
|
|
178
|
+
:param container: Container, the docker container object.
|
|
179
|
+
:return:
|
|
180
|
+
"""
|
|
181
|
+
if container.status == "running":
|
|
182
|
+
print_api(f"Stopping container: [{container.name}]. Short ID: [{container.short_id}]")
|
|
183
|
+
container.stop()
|
|
184
|
+
container.remove()
|
|
175
185
|
"""
|
|
176
186
|
Remove all containers by image name.
|
|
177
187
|
:param image_name: str, the name of the image.
|
|
@@ -179,8 +189,8 @@ def stop_remove_containers_by_image_name(image_name: str):
|
|
|
179
189
|
"""
|
|
180
190
|
client = docker.from_env()
|
|
181
191
|
all_containers = client.containers.list(all=True)
|
|
182
|
-
for
|
|
183
|
-
if any(image_name in tag for tag in
|
|
192
|
+
for current_container in all_containers:
|
|
193
|
+
if any(image_name in tag for tag in current_container.image.tags):
|
|
184
194
|
if container.status == "running":
|
|
185
195
|
print_api(f"Stopping container: [{container.name}]. Short ID: [{container.short_id}]")
|
|
186
196
|
container.stop()
|
|
@@ -231,7 +241,7 @@ def start_container_without_stop(
|
|
|
231
241
|
return client, container
|
|
232
242
|
|
|
233
243
|
|
|
234
|
-
def run_command_in_running_container(container: Container, command: list) -> tuple[int,
|
|
244
|
+
def run_command_in_running_container(container: Container, command: list) -> tuple[int, str]:
|
|
235
245
|
"""
|
|
236
246
|
Run a command in a running container.
|
|
237
247
|
:param container: Container, the docker container object.
|
|
@@ -240,22 +250,8 @@ def run_command_in_running_container(container: Container, command: list) -> tup
|
|
|
240
250
|
"""
|
|
241
251
|
|
|
242
252
|
# Run the command inside the already running container
|
|
243
|
-
|
|
253
|
+
status_code, output_bytes = container.exec_run(cmd=command, stdout=True, stderr=True)
|
|
244
254
|
# Capture logs
|
|
245
|
-
output_text =
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
execution_result_message = f"Container execution result:\n{output_text}"
|
|
249
|
-
|
|
250
|
-
if exec_result.exit_code != 0:
|
|
251
|
-
# logging.warning(f"Extraction command returned code {exec_result.exit_code} for '{filename}'")
|
|
252
|
-
code_message = f"Extraction command returned code {exec_result.exit_code}"
|
|
253
|
-
else:
|
|
254
|
-
code_message = "Extraction succeeded"
|
|
255
|
-
|
|
256
|
-
if execution_result_message:
|
|
257
|
-
execution_result_message += f"\n{code_message}"
|
|
258
|
-
else:
|
|
259
|
-
execution_result_message = code_message
|
|
260
|
-
|
|
261
|
-
return exec_result.exit_code, exec_result.output, execution_result_message
|
|
255
|
+
output_text = output_bytes.decode("utf-8", errors="replace")
|
|
256
|
+
|
|
257
|
+
return status_code, output_text
|
|
@@ -859,6 +859,7 @@ def find(
|
|
|
859
859
|
# if items:
|
|
860
860
|
# collection_items = collection_items.skip(skip_items).limit(items)
|
|
861
861
|
|
|
862
|
+
# List consolidates the results into a list of dictionaries, collection_items cursor will not be available after this.
|
|
862
863
|
entries: list[dict] = list(collection_items)
|
|
863
864
|
|
|
864
865
|
if entries and convert_object_id_to_str and '_id' in entries[0]:
|
|
@@ -4,6 +4,8 @@ import socket
|
|
|
4
4
|
|
|
5
5
|
import psutil
|
|
6
6
|
|
|
7
|
+
from ... import networks
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
def get_process_using_port(ip_port: str) -> Union[dict, None]:
|
|
9
11
|
"""
|
|
@@ -61,7 +63,7 @@ def get_default_connection_name() -> Union[dict, None]:
|
|
|
61
63
|
"""
|
|
62
64
|
# Get all interfaces.
|
|
63
65
|
interfaces: dict = psutil.net_if_addrs()
|
|
64
|
-
default_ip_address: str =
|
|
66
|
+
default_ip_address: str = networks.get_default_internet_ipv4()
|
|
65
67
|
|
|
66
68
|
for interface, details in interfaces.items():
|
|
67
69
|
for address in details:
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from win32com.client import CDispatch
|
|
2
|
+
|
|
3
|
+
from . import wmi_helpers
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def set_skip_as_source(
|
|
7
|
+
ip_addresses: list[str],
|
|
8
|
+
enable: bool = True,
|
|
9
|
+
wmi_instance: CDispatch = None
|
|
10
|
+
) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Toggle SkipAsSource for every address in *ip_addrs*.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
ip_addresses : list/tuple/iterable of str
|
|
17
|
+
One or more literal IP strings, e.g. "192.168.157.3"
|
|
18
|
+
enable : bool
|
|
19
|
+
True → behave like Set‑NetIPAddress ‑SkipAsSource $true
|
|
20
|
+
False → behave like Set‑NetIPAddress ‑SkipAsSource $false
|
|
21
|
+
wmi_instance : CDispatch
|
|
22
|
+
WMI instance to use. If not provided, a new one will be created.
|
|
23
|
+
'root\\StandardCimv2'
|
|
24
|
+
|
|
25
|
+
================
|
|
26
|
+
|
|
27
|
+
Explanation.
|
|
28
|
+
When you add extra IPv4 addresses to the same NIC, Windows treats them all as “first‑class” unless you tell it otherwise.
|
|
29
|
+
Because the two new addresses (192.168.157.3 and .4) are numerically lower than the original one (.129), the TCP/IP stack now prefers one of those lower addresses as the default source for any socket whose program didn’t bind an explicit local IP.
|
|
30
|
+
|
|
31
|
+
What that looks like on the wire
|
|
32
|
+
Client sends SYN → 192.168.157.3 (or .4).
|
|
33
|
+
– Server replies with SYN/ACK ←192.168.157.3 → handshake completes, HTTP works.
|
|
34
|
+
|
|
35
|
+
Client sends SYN → 192.168.157.129.
|
|
36
|
+
– Stack still picks .3 as its favourite and answers SYN/ACK ← 192.168.157.3.
|
|
37
|
+
– Client discards the packet (wrong IP), retransmits the SYN, your code’s accept() wakes up again, and you see an “infinite accept loop”.
|
|
38
|
+
|
|
39
|
+
The flag that fixes it: SkipAsSource
|
|
40
|
+
Tell Windows not to use the extra addresses unless an application asks for them.
|
|
41
|
+
|
|
42
|
+
PowerShell.
|
|
43
|
+
# One‑off: mark the addresses you already added
|
|
44
|
+
Get-NetIPAddress -IPAddress 192.168.157.3 | Set-NetIPAddress -SkipAsSource $true
|
|
45
|
+
Get-NetIPAddress -IPAddress 192.168.157.4 | Set-NetIPAddress -SkipAsSource $true
|
|
46
|
+
|
|
47
|
+
# —OR— add new addresses the right way from the start
|
|
48
|
+
New-NetIPAddress -InterfaceAlias "Ethernet0" `
|
|
49
|
+
-IPAddress 192.168.157.3 `
|
|
50
|
+
-PrefixLength 24 `
|
|
51
|
+
-SkipAsSource $true
|
|
52
|
+
SkipAsSource = $true keeps the address fully routable for incoming traffic and lets programs bind to it explicitly.
|
|
53
|
+
|
|
54
|
+
Windows will never choose that address as the source of an outgoing packet unless the program bound the socket to it.
|
|
55
|
+
|
|
56
|
+
After you flip the flag (no reboot required) the three‑way handshake is symmetrical again and the endless accept() loop disappears.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
if not wmi_instance:
|
|
60
|
+
wmi_instance, _ = wmi_helpers.get_wmi_instance(namespace='root\\StandardCimv2')
|
|
61
|
+
|
|
62
|
+
for ip in ip_addresses:
|
|
63
|
+
query = f"SELECT * FROM MSFT_NetIPAddress WHERE IPAddress='{ip}'"
|
|
64
|
+
matches = wmi_instance.ExecQuery(query)
|
|
65
|
+
if not matches:
|
|
66
|
+
print(f"[!] {ip}: no such address found")
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
for obj in matches: # usually just one
|
|
70
|
+
if bool(obj.SkipAsSource) == enable:
|
|
71
|
+
print(f"[=] {ip}: SkipAsSource already {enable}")
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
obj.SkipAsSource = enable
|
|
75
|
+
obj.Put_() # commit the change
|
|
76
|
+
print(f"[+] {ip}: SkipAsSource set to {enable}")
|