ntnput 1.0.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.
ntnput-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.3
2
+ Name: ntnput
3
+ Version: 1.0.0
4
+ Summary: Minimalist Python library using wraps of undocumented `Nt` functions to interact with mouse and keyboard stealthy
5
+ Author: Xenely
6
+ Requires-Python: >=3.10
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Description-Content-Type: text/markdown
13
+
14
+ # ntnput
15
+ The Minimalist Python library for Windows using wraps of undocumented `Nt` functions to interact with mouse and keyboard stealthy.
16
+
17
+ # Details
18
+ This library uses syscall wraps of undocumented `NtUserInjectMouseInput` and `NtUserInjectKeyboardInput` functions from `win32u.dll` module.
19
+ It's makes keyboard and mouse interaction safe and stealth due to the bypass of Windows triggers.</br>
20
+
21
+ **NtNput** also works faster than analogues because of usage of builtin `ctypes` library allowing to interact directly with C and machine code.
22
+ You can use this library if your process blocks usage of `mouse_event`, `SendInput` or etc. WinAPI functions.</br>
23
+
24
+ ## Installation
25
+ 1. You can install library using pip:
26
+ ```
27
+ pip install ntnput
28
+ ```
29
+ 2. You can download this repo, put it's folder into your project and import it using the folder name
30
+
31
+ ## Usage
32
+ Library provides several functions to interact with mouse:
33
+ 1. `mouse_move(x, y)` - moves mouse from current position
34
+ 2. `mouse_move_to(x, y)` - moves mouse to absolute x, y position
35
+ 3. `mouse_click(button <default "left">)` - clicks mouse
36
+ 4. `mouse_release(button <default "left">)` - releases mouse
37
+ 5. `mouse_click_and_release(button <default "left">, delay_ms <default 0.0>)` - clicks mouse, sleeps, releases
38
+
39
+ And several functions to interact with keyboard:
40
+ 1. `keyboard_press(key_code)` - presses keyboard button
41
+ 2. `keyboard_release(key_code)` - releases keyboard button
42
+ 3. `keyboard_press_and_release(key_code, delay_ms <default 0.0>)` - presses keyboard button, sleeps, releases
43
+
44
+ You can use official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes) to find keyboard key codes definitions.
45
+
ntnput-1.0.0/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # ntnput
2
+ The Minimalist Python library for Windows using wraps of undocumented `Nt` functions to interact with mouse and keyboard stealthy.
3
+
4
+ # Details
5
+ This library uses syscall wraps of undocumented `NtUserInjectMouseInput` and `NtUserInjectKeyboardInput` functions from `win32u.dll` module.
6
+ It's makes keyboard and mouse interaction safe and stealth due to the bypass of Windows triggers.</br>
7
+
8
+ **NtNput** also works faster than analogues because of usage of builtin `ctypes` library allowing to interact directly with C and machine code.
9
+ You can use this library if your process blocks usage of `mouse_event`, `SendInput` or etc. WinAPI functions.</br>
10
+
11
+ ## Installation
12
+ 1. You can install library using pip:
13
+ ```
14
+ pip install ntnput
15
+ ```
16
+ 2. You can download this repo, put it's folder into your project and import it using the folder name
17
+
18
+ ## Usage
19
+ Library provides several functions to interact with mouse:
20
+ 1. `mouse_move(x, y)` - moves mouse from current position
21
+ 2. `mouse_move_to(x, y)` - moves mouse to absolute x, y position
22
+ 3. `mouse_click(button <default "left">)` - clicks mouse
23
+ 4. `mouse_release(button <default "left">)` - releases mouse
24
+ 5. `mouse_click_and_release(button <default "left">, delay_ms <default 0.0>)` - clicks mouse, sleeps, releases
25
+
26
+ And several functions to interact with keyboard:
27
+ 1. `keyboard_press(key_code)` - presses keyboard button
28
+ 2. `keyboard_release(key_code)` - releases keyboard button
29
+ 3. `keyboard_press_and_release(key_code, delay_ms <default 0.0>)` - presses keyboard button, sleeps, releases
30
+
31
+ You can use official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes) to find keyboard key codes definitions.
@@ -0,0 +1,148 @@
1
+ # +-------------------------------------+
2
+ # | ~ Author : Xenely ~ |
3
+ # +=====================================+
4
+ # | GitHub: https://github.com/Xenely14 |
5
+ # | Discord: xenely |
6
+ # +-------------------------------------+
7
+
8
+ import typing
9
+ import ctypes
10
+ import ctypes.wintypes
11
+
12
+ # Local imports
13
+ from . import misc
14
+
15
+ # ==-------------------------------------------------------------------== #
16
+ # Global and static variables, constants #
17
+ # ==-------------------------------------------------------------------== #
18
+ LEFT_DOWN = 0x02
19
+ LEFT_UP = 0x04
20
+ RIGHT_DOWN = 0x10
21
+ RIGHT_UP = 0x08
22
+
23
+ KEY_DOWN = 0x00
24
+ KEY_UP = 0x02
25
+
26
+
27
+ # ==-------------------------------------------------------------------== #
28
+ # С-structures #
29
+ # ==-------------------------------------------------------------------== #
30
+ class InjectInputMouseInfo(ctypes.Structure):
31
+ """Structure containing information about mouse input injection."""
32
+
33
+ _fields_ = [
34
+ ("x_direction", ctypes.c_int),
35
+ ("y_direction", ctypes.c_int),
36
+ ("mouse_data", ctypes.c_uint),
37
+ ("mouse_options", ctypes.c_int),
38
+ ("time_offset_in_miliseconds", ctypes.c_uint),
39
+ ("extra_info", ctypes.c_void_p)
40
+ ]
41
+
42
+
43
+ class KeybdInput(ctypes.Structure):
44
+ """Structure containing information about keyboard input injection."""
45
+
46
+ _fields_ = [
47
+ ("vk_code", ctypes.c_ushort),
48
+ ("scan_code", ctypes.c_ushort),
49
+ ("dw_flags", ctypes.c_ulong),
50
+ ("time", ctypes.c_ulong),
51
+ ("extra_info", ctypes.c_void_p)
52
+ ]
53
+
54
+
55
+ # ==-------------------------------------------------------------------== #
56
+ # Wrapped syscall functions #
57
+ # ==-------------------------------------------------------------------== #
58
+ _NtDelayExecution = misc.syscall("NtDelayExecution", result_type=ctypes.c_ulong, arguments_types=[ctypes.c_int, ctypes.POINTER(ctypes.wintypes.LARGE_INTEGER)], module=b"ntdll.dll")
59
+ _NtUserInjectMouseInput = misc.syscall("NtUserInjectMouseInput", result_type=ctypes.c_ulong, arguments_types=[ctypes.POINTER(InjectInputMouseInfo), ctypes.c_int], module=b"win32u.dll")
60
+ _NtUserInjectKeyboardInput = misc.syscall("NtUserInjectKeyboardInput", result_type=ctypes.c_ulong, arguments_types=[ctypes.POINTER(KeybdInput), ctypes.c_int], module=b"win32u.dll")
61
+
62
+
63
+ # ==-------------------------------------------------------------------== #
64
+ # Functions #
65
+ # ==-------------------------------------------------------------------== #
66
+ def mouse_move(x: int, y: int) -> None:
67
+ """Moves mouse relative to it's current position using wrapped syscall `NtUserInjectMouseInput` function."""
68
+
69
+ # Moving mouse
70
+ _NtUserInjectMouseInput(ctypes.byref(InjectInputMouseInfo(x_direction=x, y_direction=-y)), 1)
71
+
72
+
73
+ def mouse_move_to(x: int, y: int) -> None:
74
+ """Moves mouse by absolute coorinate position using wrapped syscall `NtUserInjectMouseInput` function."""
75
+
76
+ # Enable process DPI awareness to retrieve `indeed` screen resolution
77
+ ctypes.windll.user32.SetProcessDPIAware()
78
+
79
+ # Retrieving screen resolutions
80
+ screen_width = ctypes.windll.user32.GetSystemMetrics(0)
81
+ screen_height = ctypes.windll.user32.GetSystemMetrics(1)
82
+
83
+ # Normalizing coordinates
84
+ normalized_x = int((x / screen_width) * 65535)
85
+ normalized_y = int((y / screen_height) * 65535)
86
+
87
+ # Moving mouse
88
+ _NtUserInjectMouseInput(ctypes.byref(InjectInputMouseInfo(x_direction=normalized_x, y_direction=normalized_y, mouse_options=0x8000)), 1)
89
+
90
+
91
+ def mouse_click(button: typing.Literal["left", "right"] = "left") -> None:
92
+ """Clicks mouse using wrapped syscall `NtUserInjectMouseInput` function."""
93
+
94
+ # If button literal is not allowed
95
+ if button not in (allowed_literals := typing.get_args(mouse_click.__annotations__["button"])):
96
+ raise Exception("Button literal is invalid, expected one of: `%s`" % ", ".join(allowed_literals))
97
+
98
+ # Clicking mouse
99
+ match button:
100
+
101
+ case "left": _NtUserInjectMouseInput(ctypes.byref(InjectInputMouseInfo(mouse_options=LEFT_DOWN)), 1)
102
+ case "right": _NtUserInjectMouseInput(ctypes.byref(InjectInputMouseInfo(mouse_options=RIGHT_DOWN)), 1)
103
+
104
+
105
+ def mouse_release(button: typing.Literal["left", "right"] = "left") -> None:
106
+ """Releases mouse using wrapped syscall `NtUserInjectMouseInput` function."""
107
+
108
+ # If button literal is not allowed
109
+ if button not in (allowed_literals := typing.get_args(mouse_click.__annotations__["button"])):
110
+ raise Exception("Button literal is invalid, expected one of: `%s`" % ", ".join(allowed_literals))
111
+
112
+ # Releasing mouse
113
+ match button:
114
+
115
+ case "left": _NtUserInjectMouseInput(ctypes.byref(InjectInputMouseInfo(mouse_options=LEFT_UP)), 1)
116
+ case "right": _NtUserInjectMouseInput(ctypes.byref(InjectInputMouseInfo(mouse_options=RIGHT_UP)), 1)
117
+
118
+
119
+ def mouse_click_and_release(button: typing.Literal["left", "right"] = "left", delay_ms: float = 0.0) -> None:
120
+ """Clicks mouse and releases after given delay time passed in milliseconds using wrapped syscall `NtUserInjectMouseInput` function."""
121
+
122
+ # If button literal is not allowed
123
+ if button not in (allowed_literals := typing.get_args(mouse_click.__annotations__["button"])):
124
+ raise Exception("Button literal is invalid, expected one of: `%s`" % ", ".join(allowed_literals))
125
+
126
+ # Clicking and releasing mouse
127
+ mouse_click(button), _NtDelayExecution(1, ctypes.byref(ctypes.wintypes.LARGE_INTEGER(int(-abs(delay_ms) * 10_000)))), mouse_release(button)
128
+
129
+
130
+ def keyboard_press(key_code: int) -> None:
131
+ """Presses keyboard key using wrapped syscall `NtUserInjectKeyboardInput` function."""
132
+
133
+ # Pressing keyboard key
134
+ _NtUserInjectKeyboardInput(ctypes.byref(KeybdInput(vk_code=key_code, dw_flags=KEY_DOWN)), 1)
135
+
136
+
137
+ def keyboard_release(key_code: int) -> None:
138
+ """Releases keyboard key using wrapped syscall `NtUserInjectKeyboardInput` function."""
139
+
140
+ # Pressing keyboard key
141
+ _NtUserInjectKeyboardInput(ctypes.byref(KeybdInput(vk_code=key_code, dw_flags=KEY_UP)), 1)
142
+
143
+
144
+ def keyboard_press_and_release(key_code: int, delay_ms: float = 0.0) -> None:
145
+ """Presses keyboard key and releases after given delay time passed in milliseconds using wrapped syscall `NtUserInjectKeyboardInput` function."""
146
+
147
+ # Clicking and releasing keyboard key
148
+ keyboard_press(key_code), _NtDelayExecution(1, ctypes.byref(ctypes.wintypes.LARGE_INTEGER(int(-abs(delay_ms) * 10_000)))), keyboard_release(key_code)
@@ -0,0 +1,102 @@
1
+ # +-------------------------------------+
2
+ # | ~ Author : Xenely ~ |
3
+ # +=====================================+
4
+ # | GitHub: https://github.com/Xenely14 |
5
+ # | Discord: xenely |
6
+ # +-------------------------------------+
7
+
8
+ import ctypes
9
+ import struct
10
+ import typing
11
+ import ctypes.wintypes
12
+
13
+ # ==-------------------------------------------------------------------== #
14
+ # DLL functions #
15
+ # ==-------------------------------------------------------------------== #
16
+
17
+ # DLL libraries loading
18
+ _kernel32 = ctypes.windll.kernel32
19
+
20
+ # DLL libraries functions loading
21
+ _LoadLibraryA = _kernel32.LoadLibraryA
22
+ _GetProcAddress = _kernel32.GetProcAddress
23
+ _VirtualProtect = _kernel32.VirtualProtect
24
+
25
+ # Defining of DLL libraries functions return type
26
+ _LoadLibraryA.restype = ctypes.wintypes.LPVOID
27
+ _GetProcAddress.restype = ctypes.wintypes.LPVOID
28
+ _VirtualProtect.restype = ctypes.wintypes.BOOL
29
+
30
+ # Defining of DLL libraries functions argument types
31
+ _LoadLibraryA .argtypes = [ctypes.wintypes.LPCSTR]
32
+ _GetProcAddress.argtypes = [ctypes.wintypes.LPVOID, ctypes.wintypes.LPCSTR]
33
+ _VirtualProtect.argtypes = [ctypes.wintypes.LPVOID, ctypes.c_size_t, ctypes.wintypes.DWORD, ctypes.POINTER(ctypes.wintypes.DWORD)]
34
+
35
+
36
+ # ==-------------------------------------------------------------------== #
37
+ # Functions #
38
+ # ==-------------------------------------------------------------------== #
39
+ def syscall(nt_function_name: str, *, result_type: typing.Any, arguments_types: list[typing.Any], module: bytes = b"ntdll.dll") -> ctypes.WINFUNCTYPE:
40
+ """Finds function in DLL module by it name, retrieves it's syscall ID, wraps it into raw function buffer and casts to `WINFUNCTYPE` to make call shadowed."""
41
+
42
+ # Module loading
43
+ if not (module_handle := _LoadLibraryA(module)):
44
+ raise Exception("Unable to load module `%s`" % module.decode())
45
+
46
+ # Retrieving nt-function pointer
47
+ if not (nt_function := _GetProcAddress(module_handle, nt_function_name.encode())):
48
+ raise Exception("Function `%s` not found" % nt_function_name)
49
+
50
+ offset = 0
51
+ syscall_id = None
52
+
53
+ # Retrieving syscall ID from nt-function pointer
54
+ while True:
55
+
56
+ # Syscall ID not found
57
+ if offset > 0x16:
58
+ break
59
+
60
+ # Retrieving syscall ID from memory
61
+ if ctypes.cast(nt_function + offset, ctypes.POINTER(ctypes.c_ubyte)).contents.value == 0xB8:
62
+ syscall_id = ctypes.cast(nt_function + offset + 1, ctypes.POINTER(ctypes.c_ushort)).contents.value
63
+ break
64
+
65
+ offset += 1
66
+
67
+ # Syscall ID not found
68
+ if syscall_id is None:
69
+ raise Exception("Syscall ID for function `%s` not found" % nt_function_name)
70
+
71
+ # Converting syscall ID to hex-bytes list
72
+ syscall_id_bytes = [hex(item)[2:] if len(hex(item)[2:]) == 2 else "0" + hex(item)[2:] for item in struct.pack("<h", syscall_id)]
73
+
74
+ # NOTE: I actually don't know why does this code require access `gs` segment.
75
+ # I've just used this repo as reference: https://github.com/opcode86/SysCaller
76
+ #
77
+ # mov rax, gs:[0x60]
78
+ # mov r10, rcx
79
+ # mov eax, <syscall id>,
80
+ # syscall
81
+ # ret
82
+ shellcode = bytes.fromhex("""
83
+ 65 48 8B 04 25 60 00
84
+ 00 00
85
+ 4C 8B D1
86
+ B8 %s %s 00 00
87
+ 0F 05
88
+ C3
89
+ """ % tuple([*syscall_id_bytes]))
90
+
91
+ # Allocating buffer for function machine code
92
+ buffer = (ctypes.c_uint8 * len(shellcode))()
93
+ shellcode_buffer = ctypes.cast(buffer, ctypes.c_void_p)
94
+
95
+ # Copying shellcode into function machine code buffer
96
+ ctypes.memmove(shellcode_buffer, ctypes.create_string_buffer(shellcode, len(shellcode)), len(shellcode))
97
+
98
+ # Updating of function machine code buffer memory protection to make it executable
99
+ ctypes.windll.kernel32.VirtualProtect(shellcode_buffer, len(shellcode), 0x40, ctypes.wintypes.LPDWORD(ctypes.wintypes.DWORD()))
100
+
101
+ # Return wrapped syscall function
102
+ return ctypes.cast(shellcode_buffer, ctypes.WINFUNCTYPE(result_type, *arguments_types))
@@ -0,0 +1,16 @@
1
+ [project]
2
+ name = "ntnput"
3
+ version = "1.0.0"
4
+ description = "Minimalist Python library using wraps of undocumented `Nt` functions to interact with mouse and keyboard stealthy"
5
+ authors = [
6
+ {name = "Xenely"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ ]
12
+
13
+
14
+ [build-system]
15
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
16
+ build-backend = "poetry.core.masonry.api"