ntmemoryapi 2.5.1__tar.gz → 2.6.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.
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ntmemoryapi
3
- Version: 2.5.1
3
+ Version: 2.6.0
4
4
  Summary: Simple library for Windows to manipulate process virtual memory with stelthy syscall wraps
5
5
  Author: Xenely
6
6
  Requires-Dist: psutil>=7.2.2
7
+ Requires-Dist: syscallwraps>=1.0.4
7
8
  Requires-Python: >=3.13
8
9
  Description-Content-Type: text/markdown
9
10
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ntmemoryapi"
3
- version = "2.5.1"
3
+ version = "2.6.0"
4
4
  description = "Simple library for Windows to manipulate process virtual memory with stelthy syscall wraps"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -9,6 +9,7 @@ authors = [
9
9
  ]
10
10
  dependencies = [
11
11
  "psutil>=7.2.2",
12
+ "syscallwraps>=1.0.4",
12
13
  ]
13
14
 
14
15
  [build-system]
@@ -10,9 +10,9 @@ import typing
10
10
  import ctypes
11
11
  import psutil
12
12
  import functools
13
+ import syscallwraps
13
14
 
14
15
  # Local imports
15
- from . import misc
16
16
  from . import embed
17
17
  from . import errors
18
18
  from . import structs
@@ -20,8 +20,6 @@ from . import structs
20
20
  # ==-------------------------------------------------------------------== #
21
21
  # Static and global variables, constans #
22
22
  # ==-------------------------------------------------------------------== #
23
- syscall_wrapper = misc.DirectSyscallWrapper()
24
-
25
23
  PROCESS_ID = 1
26
24
  PROCESS_NAME = 2
27
25
 
@@ -101,7 +99,7 @@ class PatternScanBuffer(ctypes.Structure):
101
99
  # ==-------------------------------------------------------------------== #
102
100
  # Syscalls #
103
101
  # ==-------------------------------------------------------------------== #
104
- syscall_wrapper = misc.DirectSyscallWrapper()
102
+ syscall_wrapper = syscallwraps.SyscallWrapper()
105
103
 
106
104
  _nt_close = syscall_wrapper.wrap("NtClose", result_type=ctypes.c_ulong, arguments_types=[ctypes.c_void_p])
107
105
  _nt_open_process = syscall_wrapper.wrap("NtOpenProcess", result_type=ctypes.c_ulong, arguments_types=[ctypes.c_void_p, ctypes.c_ulong, ctypes.POINTER(structs.OBJECT_ATTRIBUTES), ctypes.POINTER(structs.CLIENT_ID)])
@@ -1,141 +0,0 @@
1
- # +-------------------------------------+
2
- # | ~ Author : Xenely ~ |
3
- # +=====================================+
4
- # | GitHub: https://github.com/Xenely14 |
5
- # | Discord: xenely |
6
- # +-------------------------------------+
7
-
8
- import ctypes
9
- import typing
10
-
11
- # ==-------------------------------------------------------------------== #
12
- # DLL functions #
13
- # ==-------------------------------------------------------------------== #
14
-
15
- # DLL libraries loading
16
- _kernel32 = ctypes.windll.kernel32
17
-
18
- # DLL libraries functions loading
19
- _LoadLibraryA = _kernel32.LoadLibraryA
20
- _GetProcAddress = _kernel32.GetProcAddress
21
- _VirtualFree = _kernel32.VirtualFree
22
- _VirtualAlloc = _kernel32.VirtualAlloc
23
- _VirtualProtect = _kernel32.VirtualProtect
24
-
25
- # Define of DLL libraries functions return type
26
- _LoadLibraryA.restype = ctypes.c_void_p
27
- _GetProcAddress.restype = ctypes.c_void_p
28
- _VirtualFree.restype = ctypes.c_long
29
- _VirtualAlloc.restype = ctypes.c_void_p
30
- _VirtualProtect.restype = ctypes.c_long
31
-
32
- # Define of DLL libraries functions argument types
33
- _LoadLibraryA .argtypes = [ctypes.c_char_p]
34
- _GetProcAddress.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
35
- _VirtualFree.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong]
36
- _VirtualAlloc.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.c_ulong]
37
- _VirtualProtect.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.POINTER(ctypes.c_ulong)]
38
-
39
-
40
- # ==-------------------------------------------------------------------== #
41
- # Classes #
42
- # ==-------------------------------------------------------------------== #
43
- class DirectSyscallWrapper:
44
- """Class to create syscall wrappers to make syscall calls """
45
-
46
- # Table containing all syscall wrappers allocations
47
- registred_syscalls_table = dict()
48
-
49
- # ==--------------------------------== #
50
- # Public methods #
51
- # ==--------------------------------== #
52
- def __init__(self) -> None:
53
- """Creates wrapper instance to wrap syscalls."""
54
-
55
- # Create list of wrapped syscalls allocations and save it into syscall table to free memory on instance delete
56
- self.registred_syscalls_table[id(self)] = dict()
57
-
58
- def __del__(self) -> None:
59
-
60
- # Get all wrapper allocations addresses and deallocate them
61
- for address in self.registred_syscalls_table[id(self)].copy().values():
62
- _VirtualFree(address, 0, 0x8000)
63
-
64
- # Clean up syscall table
65
- del self.registred_syscalls_table[id(self)]
66
-
67
- def wrap(self, function_name: str, *, result_type: typing.Any, arguments_types: list[typing.Any], search_module: bytes = b"ntdll.dll") -> ctypes.WINFUNCTYPE:
68
- """Retrieves syscall ID by function name, wraps it into raw function buffer and casts to `WINFUNCTYPE` to make stealthy-callable."""
69
-
70
- # If syscall wrap already exists
71
- if (syscall_wrap := self.registred_syscalls_table[id(self)].get(function_name)) is not None:
72
- return syscall_wrap
73
-
74
- # Module loading
75
- if not (module_handle := _LoadLibraryA(search_module)):
76
- raise ctypes.WinError(descr="Unable to load module `%s`" % search_module.decode())
77
-
78
- # Retrieve function pointer
79
- if not (serach_function := _GetProcAddress(module_handle, function_name.encode())):
80
- raise ctypes.WinError(descr="Function `%s` not found" % function_name)
81
-
82
- # Syscall id
83
- syscall_id = None
84
-
85
- # Retrieve syscall ID from function pointer
86
- for index in range(0x16 + 1):
87
-
88
- # If syscall ID found
89
- if ctypes.cast(serach_function + index, ctypes.POINTER(ctypes.c_ubyte)).contents.value == 0xB8:
90
-
91
- syscall_id = ctypes.cast(serach_function + index + 1, ctypes.POINTER(ctypes.c_ushort)).contents.value
92
- break
93
-
94
- # If syscall ID not found
95
- if syscall_id is None:
96
- raise ValueError("Syscall ID for function `%s` not found" % function_name)
97
-
98
- # Convert syscall ID to hex-bytes list
99
- syscall_id_bytes = [hex(item)[2:] if len(hex(item)[2:]) == 2 else "0" + hex(item)[2:] for item in bytes(ctypes.c_ushort(syscall_id))]
100
-
101
- # Create bytes buffer with syscall ID inlined
102
- # NOTE: `ntdll.dll` functions begins the same way, so it's just replica to behave the same.
103
- #
104
- # mov r10, rcx
105
- # mov eax, <syscall id>,
106
- # syscall
107
- # ret
108
- shellcode = bytes.fromhex("""
109
- 4C 8B D1
110
- B8 %s %s 00 00
111
- 0F 05
112
- C3
113
- """ % tuple(syscall_id_bytes))
114
-
115
- # Allocate buffer for function machine code
116
- if not (shellcode_buffer := _VirtualAlloc(0, len(shellcode), 0x1000 | 0x2000, 0x04)):
117
- raise ctypes.WinError(descr="Unable to alloate memory for shellcode")
118
-
119
- # Save allocated buffer into wrapped syscalls table
120
- self.registred_syscalls_table[id(self)][function_name] = shellcode_buffer
121
-
122
- # Copy shellcode into function machine code buffer
123
- ctypes.memmove(shellcode_buffer, ctypes.create_string_buffer(shellcode, len(shellcode)), len(shellcode))
124
-
125
- # Update of function machine code buffer memory protection to make it executable
126
- ctypes.windll.kernel32.VirtualProtect(shellcode_buffer, len(shellcode), 0x20, ctypes.byref(ctypes.c_ulong()))
127
-
128
- # Return wrapped syscall function
129
- return ctypes.cast(shellcode_buffer, ctypes.WINFUNCTYPE(result_type, *arguments_types))
130
-
131
- def clean_up(self, function_name: str) -> None:
132
- """Cleans up syscall wrapper, deallocates it's shellcode buffer."""
133
-
134
- # If function found
135
- if (address := self.registred_syscalls_table[id(self)].get(function_name)) is not None:
136
-
137
- # Deallocate syscall byffer
138
- _VirtualFree(address)
139
-
140
- # Clean up syscall table
141
- del self.registred_syscalls_table[id(self)][function_name]
File without changes