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 +45 -0
- ntnput-1.0.0/README.md +31 -0
- ntnput-1.0.0/ntnput/__init__.py +148 -0
- ntnput-1.0.0/ntnput/misc.py +102 -0
- ntnput-1.0.0/pyproject.toml +16 -0
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"
|