NtUserInjectKeyboardInput函数绕过杀软屏幕锁定模拟键鼠
本人声明,文章所涉及的技术仅供学术研究,任何法律责任与作者无关
NtUserInjectKeyboardInput函数绕过杀软屏幕锁定模拟键鼠
0.NtUserInjectKeyboardInput函数介绍:
NtUserInjectKeyboardInput是 Windows 内部的 API,属于
NtUser系列函数之一。这个函数并不是公开的文档函数,因此它没有广泛的开发者文档。它属于较低层次的系统调用,用于模拟键盘输入。由于它是 Windows 内部 API,微软并没有在标准 SDK 中提供详细的公开文档。这个函数一般用于模拟键盘事件或直接注入键盘输入到操作系统中,允许开发者模拟键盘按键的触发。
你通常会在一些自瞄锁头这种外挂上见到这个函数,因为,他能绕过各种杀软的屏幕锁定模拟键盘鼠标行为。
1.原理补充:
在所有类windows系统下键盘模拟,虚拟键盘优先级别占到最高,通俗的讲就是这东西始终是在屏幕至顶层,而NtUserInjectKeyboardInput的作用恰恰就是注入虚拟键盘模拟指令,所以NtUserInjectKeyboardInput函数在所有键盘模拟函数中优先级约等于最高。
2.函数原型:
NTSTATUS NtUserInjectKeyboardInput(
ULONG dwFlags,
PKEYBDINPUT pKeyBdInput,
ULONG cInputs
);
解释:
A.dwFlags:指定注入的标志。通常为 0,表示正常的键盘输入。
B.pKeyBdInput:指向一个KEYBDINPUT结构的指针,描述了一个具体的键盘事件。
C.KEYBDINPUT包含了按键的虚拟键码、按下/释放的状态等信息。
D.cInputs:输入的键盘事件数目。这个值告诉函数需要注入多少个键盘事件。
3.KEYBDINPUT 结构:用来描述键盘输入事件的具体信息
结构体定义:
typedef struct tagKEYBDINPUT {
USHORT wVk; // 虚拟键码
USHORT wScan; // 扫描码
DWORD dwFlags; // 标志,控制键盘事件类型(如按下或释放)
DWORD time; // 时间戳,通常为 0
ULONG_PTR dwExtraInfo; // 附加信息,通常为 0
} KEYBDINPUT;
4.dwFlags字段标志内容:
A.KEYEVENTF_KEYDOWN:按下键。
B.KEYEVENTF_KEYUP:释放键。
C.KEYEVENTF_SCANCODE:指示wScan字段使用扫描码。
5.虚拟键码表:
键名 键码 (Virtual Key Code)
A 0x41
B 0x42
C 0x43
D 0x44
E 0x45
F 0x46
G 0x47
H 0x48
I 0x49
J 0x4A
K 0x4B
L 0x4C
M 0x4D
N 0x4E
O 0x4F
P 0x50
Q 0x51
R 0x52
S 0x53
T 0x54
U 0x55
V 0x56
W 0x57
X 0x58
Y 0x59
Z 0x5A
0 0x30
1 0x31
2 0x32
3 0x33
4 0x34
5 0x35
6 0x36
7 0x37
8 0x38
9 0x39
F1 0x70
F2 0x71
F3 0x72
F4 0x73
F5 0x74
F6 0x75
F7 0x76
F8 0x77
F9 0x78
F10 0x79
F11 0x7A
F12 0x7B
Enter 0x0D
Esc 0x1B
Spacebar 0x20
Tab 0x09
Backspace 0x08
Ctrl 0x11
Alt 0x12
Shift 0x10
Caps Lock 0x14
Arrow Up 0x26
Arrow Down 0x28
Arrow Left 0x25
Arrow Right 0x27
Delete 0x2E
Insert 0x2D
Home 0x24
End 0x23
Page Up 0x21
Page Down 0x22
6.对于模拟键盘的demo
#include <Windows.h>
#include <iostream>
int main() {
// 创建一个 KEYBDINPUT 结构
KEYBDINPUT ki = {0};
ki.wVk = 0x41; // A 键的虚拟键码
ki.dwFlags = KEYEVENTF_KEYDOWN; // 按下键
ki.wScan = MapVirtualKey(0x41, MAPVK_VK_TO_SCAN); // 获取扫描码
// 设定 INPUT 结构
INPUT input = {0};
input.type = INPUT_KEYBOARD;
input.ki = ki;
// 调用 NtUserInjectKeyboardInput 进行键盘输入注入
ULONG cInputs = 1;
NtUserInjectKeyboardInput(0, &input.ki, cInputs);
// 模拟释放 A 键
ki.dwFlags = KEYEVENTF_KEYUP; // 释放键
NtUserInjectKeyboardInput(0, &input.ki, cInputs);
std::cout << "键盘输入注入完成" << std::endl;
return 0;
}
7.对于模拟鼠标的demo
注意,这玩意是用于模拟键盘输入的函数,无法直接用于模拟鼠标运动,所以,本文的重点来了:NtUserInjectKeyboardInput对于左键的替代(enter)+SendInput 移动鼠标到指定位置,同样可以达到类似(NtUserInjectMouseInput(不存在))这种函数的效果,即最高优先级的鼠标模拟,具体模拟鼠标操作demo如下(选自鄙人之前写的一个killer):
#include <windows.h>
#include <iostream>
#include <thread>
#include <cstdlib> // 用于rand()
#include <ctime> // 用于time()
// 模拟延时
void delay(int milliseconds) {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
// 模拟鼠标点击
void clickAt(int x, int y) {
INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dx = x;
input.mi.dy = y;
input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
SendInput(1, &input, sizeof(INPUT)); // 移动鼠标
delay(10); // 短暂延时
// 鼠标左键按下
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &input, sizeof(INPUT));
delay(10); // 短暂延时
// 鼠标左键抬起
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1, &input, sizeof(INPUT));
}
// 获取屏幕键盘上的“Enter”键的坐标(这里需要根据实际情况调整)
POINT getScreenKeyboardEnterKeyPosition() {
POINT enterKeyPosition = { 50000, 20000 };
// 这是一个示例位置
return enterKeyPosition;
}
int main() {
// 初始化随机种子
std::srand(static_cast<unsigned int>(std::time(nullptr)));
// Step 1: 打开指定程序
system("start xx\\uninst.exe");
delay(1000); // 等待窗口打开
// Step 2: 获取窗口位置和尺寸
POINT windowCenter;
HWND hwnd = FindWindow(NULL, TEXT("xxxx")); // 替换为实际窗口标题
if (!hwnd) {
std::cerr << "cant find" << std::endl;
return -1;
}
RECT rect;
GetWindowRect(hwnd, &rect);
windowCenter.x = (rect.left + rect.right) / 2;
windowCenter.y = (rect.top + rect.bottom) / 2;
// Step 3: 模拟鼠标移动到窗口中心
SetCursorPos(windowCenter.x, windowCenter.y);
delay(10); // 等待鼠标定位
// Step 4: 移动操作,如
SetCursorPos(windowCenter.x - 50, windowCenter.y + 50); // 左移50像素,下移50像素
delay(10); // 等待鼠标定位
// Step 5: 打开屏幕键盘
system("start osk.exe"); // 启动屏幕键盘
delay(200); // 等待屏幕键盘打开
// 获取屏幕键盘上的 "Enter" 键的坐标
POINT enterKeyPosition = getScreenKeyboardEnterKeyPosition();
// Step 6: 模拟鼠标点击操作并触发屏幕键盘上的 Enter 键
for (int i = 0; i < 5; i++) { // 0.1秒间隔点击,共点击50次
// 点击目标窗口位置
INPUT input = { 0 };
input.type = INPUT_MOUSE;
// 小幅度随机左右移动
int randomOffset = std::rand() % 5; // 随机产生0到4的左右偏移
int direction = (std::rand() % 2 == 0) ? -1 : 1; // 随机选择方向
// 更新鼠标位置
SetCursorPos(windowCenter.x - 50 + randomOffset * direction, windowCenter.y + 50);
// 模拟鼠标左键按下
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; // 鼠标左键按下
SendInput(1, &input, sizeof(INPUT)); // 执行鼠标点击按下
delay(50); // 短暂延时模拟按下的时间
// 模拟鼠标左键抬起
input.mi.dwFlags = MOUSEEVENTF_LEFTUP; // 鼠标左键抬起
SendInput(1, &input, sizeof(INPUT)); // 执行鼠标点击抬起
delay(50); // 0.1 秒的延时
// 模拟点击屏幕键盘上的 Enter 键
clickAt(enterKeyPosition.x, enterKeyPosition.y);
}
std::cout << "操作完成!" << std::endl;
return 0;
}
Ps:这是后话,鄙人在发表这篇拙见时不幸遭遇雷电法王220V的攻击,具体体现为lz充电线漏电了,我还说我胳臂怎么这么刺挠呢,一抬胳臂全麻了,mpp.......444444444444444444444444
0 条评论
可输入 255 字