ntmemoryapi 1.1.0__tar.gz → 1.2.0__tar.gz
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.
- {ntmemoryapi-1.1.0 → ntmemoryapi-1.2.0}/PKG-INFO +2 -1
- {ntmemoryapi-1.1.0 → ntmemoryapi-1.2.0}/ntmemoryapi/__init__.py +30 -76
- {ntmemoryapi-1.1.0 → ntmemoryapi-1.2.0}/pyproject.toml +7 -3
- {ntmemoryapi-1.1.0 → ntmemoryapi-1.2.0}/README.md +0 -0
- {ntmemoryapi-1.1.0 → ntmemoryapi-1.2.0}/ntmemoryapi/embed.py +0 -0
- {ntmemoryapi-1.1.0 → ntmemoryapi-1.2.0}/ntmemoryapi/misc.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ntmemoryapi
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Simple library for Windows to manipulate process virtual memory with stelthy syscall wraps
|
|
5
5
|
Author: Xenely
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Requires-Dist: psutil (>=7.1.0,<8.0.0)
|
|
12
13
|
Description-Content-Type: text/markdown
|
|
13
14
|
|
|
14
15
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
9
|
import ctypes
|
|
10
|
+
import psutil
|
|
10
11
|
|
|
11
12
|
# Local imports
|
|
12
13
|
from .misc import *
|
|
@@ -83,35 +84,6 @@ class OBJECT_ATTRIBUTES(ctypes.Structure):
|
|
|
83
84
|
]
|
|
84
85
|
|
|
85
86
|
|
|
86
|
-
class PROCESSENTRY32(ctypes.Structure):
|
|
87
|
-
"""Process entry structure for processes enumeration."""
|
|
88
|
-
|
|
89
|
-
_fields_ = [
|
|
90
|
-
("dw_size", ctypes.c_ulong),
|
|
91
|
-
("cnt_usage", ctypes.c_ulong),
|
|
92
|
-
("th32_process_id", ctypes.c_ulong),
|
|
93
|
-
("th32_default_heap_id", ctypes.c_void_p),
|
|
94
|
-
("th32_module_id", ctypes.c_ulong),
|
|
95
|
-
("cnt_threads", ctypes.c_ulong),
|
|
96
|
-
("th32_parent_process_id", ctypes.c_ulong),
|
|
97
|
-
("pc_pri_class_base", ctypes.c_long),
|
|
98
|
-
("dw_flags", ctypes.c_ulong),
|
|
99
|
-
("sz_exe_file", ctypes.c_char * 260)
|
|
100
|
-
]
|
|
101
|
-
|
|
102
|
-
@property
|
|
103
|
-
def pid(self) -> int:
|
|
104
|
-
"""Process ID."""
|
|
105
|
-
|
|
106
|
-
return self.th32_process_id
|
|
107
|
-
|
|
108
|
-
@property
|
|
109
|
-
def name(self) -> str:
|
|
110
|
-
"""Process name."""
|
|
111
|
-
|
|
112
|
-
return self.sz_exe_file.decode()
|
|
113
|
-
|
|
114
|
-
|
|
115
87
|
class MODULEENTRY32(ctypes.Structure):
|
|
116
88
|
"""Module entry structure, describes an entry from a list of the modules belonging to the specified process."""
|
|
117
89
|
|
|
@@ -254,61 +226,43 @@ _nt_protect_virtual_memory = syscall("NtProtectVirtualMemory", result_type=ctype
|
|
|
254
226
|
# ==-------------------------------------------------------------------== #
|
|
255
227
|
# Functions #
|
|
256
228
|
# ==-------------------------------------------------------------------== #
|
|
257
|
-
def list_processes(include_id: bool = True, include_name: bool = True) -> list[dict[str, int | str]]:
|
|
229
|
+
def list_processes(include_id: bool = True, include_name: bool = True, include_username: bool = True, current_username_only: bool = True) -> list[dict[str, int | str]]:
|
|
258
230
|
"""List all of the currently active system processes."""
|
|
259
231
|
|
|
260
232
|
# If `include_id` and `include_name` disabled both
|
|
261
233
|
if not include_id and not include_name:
|
|
262
234
|
raise Exception("Unable to disable ID and name including at once")
|
|
263
235
|
|
|
264
|
-
#
|
|
265
|
-
|
|
266
|
-
process32_next = ctypes.windll.kernel32.Process32Next
|
|
267
|
-
process32_first = ctypes.windll.kernel32.Process32First
|
|
268
|
-
create_tool_help32_snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
|
|
269
|
-
|
|
270
|
-
# Create snapshot to iterate processes
|
|
271
|
-
if (snapshot := create_tool_help32_snapshot(TH32CS_SNAPPROCESS, 0)) == -1:
|
|
272
|
-
raise Exception("Unable to create snapshot to iterate processes")
|
|
236
|
+
# Current process username
|
|
237
|
+
current_username = psutil.Process().username()
|
|
273
238
|
|
|
274
239
|
# Result list of processes
|
|
275
240
|
processes = list()
|
|
276
241
|
|
|
277
|
-
# Process
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
# Process entry to iterate processes
|
|
281
|
-
process_entry = PROCESSENTRY32()
|
|
282
|
-
process_entry.dw_size = ctypes.sizeof(PROCESSENTRY32)
|
|
283
|
-
|
|
284
|
-
# Retrieve first process snapshot
|
|
285
|
-
if not process32_first(snapshot, ctypes.byref(process_entry)):
|
|
286
|
-
raise Exception("Unable to get first process to save to snapshot")
|
|
242
|
+
# Process iteration
|
|
243
|
+
for process in psutil.process_iter():
|
|
287
244
|
|
|
288
|
-
#
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
# Save process to list
|
|
292
|
-
process_dict = dict()
|
|
245
|
+
# If process started not from current username
|
|
246
|
+
if current_username_only and process.username() != current_username:
|
|
247
|
+
continue
|
|
293
248
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
process_dict |= {"id": process_entry.pid}
|
|
249
|
+
# Save process to list
|
|
250
|
+
process_dict = dict()
|
|
297
251
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
252
|
+
# If process ID including required
|
|
253
|
+
if include_id:
|
|
254
|
+
process_dict |= {"id": process.pid}
|
|
301
255
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if not process32_next(snapshot, ctypes.byref(process_entry)):
|
|
306
|
-
break
|
|
256
|
+
# If process ID including required
|
|
257
|
+
if include_name:
|
|
258
|
+
process_dict |= {"name": process.name()}
|
|
307
259
|
|
|
308
|
-
|
|
260
|
+
# If process username including required
|
|
261
|
+
if include_username:
|
|
262
|
+
process_dict |= {"username": process.username()}
|
|
309
263
|
|
|
310
|
-
#
|
|
311
|
-
|
|
264
|
+
# Save process information to list
|
|
265
|
+
processes.append(process_dict)
|
|
312
266
|
|
|
313
267
|
return processes
|
|
314
268
|
|
|
@@ -323,17 +277,17 @@ class Process:
|
|
|
323
277
|
# Methods #
|
|
324
278
|
# ==-------------------------------------------------------------------== #
|
|
325
279
|
|
|
326
|
-
def __init__(self, pid_or_name: int | str, access: int = PROCESS_ALL_ACCESS) -> None:
|
|
280
|
+
def __init__(self, pid_or_name: int | str, access: int = PROCESS_ALL_ACCESS, current_username_only: bool = True) -> None:
|
|
327
281
|
"""Initialize instance to manipulate process."""
|
|
328
282
|
|
|
329
283
|
# Open process by it's ID or it's name
|
|
330
284
|
match pid_or_name:
|
|
331
285
|
|
|
332
286
|
case pid if type(pid_or_name) is int:
|
|
333
|
-
self.handle, self.name, self.pid = self.__init_with_pid(pid, access)
|
|
287
|
+
self.handle, self.name, self.pid = self.__init_with_pid(pid, access, current_username_only)
|
|
334
288
|
|
|
335
289
|
case name if type(pid_or_name) is str:
|
|
336
|
-
self.handle, self.name, self.pid = self.__init_with_name(name, access)
|
|
290
|
+
self.handle, self.name, self.pid = self.__init_with_name(name, access, current_username_only)
|
|
337
291
|
|
|
338
292
|
case _:
|
|
339
293
|
raise Exception("Invalid `pid_or_name` argument value, have to be `int` or `str` type")
|
|
@@ -874,11 +828,11 @@ class Process:
|
|
|
874
828
|
# ==-------------------------------------------------------------------== #
|
|
875
829
|
# Private methods #
|
|
876
830
|
# ==-------------------------------------------------------------------== #
|
|
877
|
-
def __init_with_pid(self, pid: int, access: int, process_name: str | None = None) -> int:
|
|
831
|
+
def __init_with_pid(self, pid: int, access: int, current_username_only: bool, process_name: str | None = None) -> int:
|
|
878
832
|
"""Open process handle by it's ID with desired access."""
|
|
879
833
|
|
|
880
834
|
# Iterate all of the processes if name not defined
|
|
881
|
-
for process in list_processes():
|
|
835
|
+
for process in list_processes(current_username_only):
|
|
882
836
|
|
|
883
837
|
# If process have a reqired name
|
|
884
838
|
if process["id"] == pid:
|
|
@@ -908,14 +862,14 @@ class Process:
|
|
|
908
862
|
|
|
909
863
|
return handle.value, process_name, pid
|
|
910
864
|
|
|
911
|
-
def __init_with_name(self, name: str, access: int) -> int:
|
|
865
|
+
def __init_with_name(self, name: str, access: int, current_username_only: bool) -> int:
|
|
912
866
|
"""Open process hanle by it's name with desired access."""
|
|
913
867
|
|
|
914
868
|
# Iterate all of the processes using snapshot
|
|
915
|
-
for process in list_processes():
|
|
869
|
+
for process in list_processes(current_username_only):
|
|
916
870
|
|
|
917
871
|
# If process have a reqired name
|
|
918
872
|
if process["name"].lower() == name.strip().lower():
|
|
919
|
-
return self.__init_with_pid(process["id"], access, process["name"])
|
|
873
|
+
return self.__init_with_pid(process["id"], access, current_username_only, process["name"])
|
|
920
874
|
|
|
921
875
|
raise Exception("Process with `%s` name not found" % name)
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ntmemoryapi"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.2.0"
|
|
4
4
|
description = "Simple library for Windows to manipulate process virtual memory with stelthy syscall wraps"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Xenely"}
|
|
7
7
|
]
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
|
-
dependencies = [
|
|
10
|
+
dependencies = [
|
|
11
|
+
"psutil (>=7.1.0,<8.0.0)"
|
|
12
|
+
]
|
|
11
13
|
|
|
12
14
|
[build-system]
|
|
13
|
-
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
14
15
|
build-backend = "poetry.core.masonry.api"
|
|
16
|
+
requires = [
|
|
17
|
+
"poetry-core>=2.0.0,<3.0.0"
|
|
18
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|