R3 应用层键盘监控主要分三种:
1、使用SetWindowsHookEx hook住WH_KEYBOARD_LL。
这种方式解决起来比较简单,就是不断hook与unhook,因为后hook的可以在先hook之前获取到通知,并选择是否传下去。
LRESULT CALLBACK LowLevelKeyboardProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam) {
KBDLLHOOKSTRUCT *ks = (KBDLLHOOKSTRUCT*)lParam;
/*typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode; // 按键代号
DWORD scanCode; // 硬件扫描代号,同 vkCode 也可以作为按键的代号。
DWORD flags; // 事件类型,一般按键按下为 0 抬起为 128。
DWORD time; // 消息时间戳
ULONG_PTR dwExtraInfo; // 消息附加信息,一般为 0。
}KBDLLHOOKSTRUCT,*LPKBDLLHOOKSTRUCT,*PKBDLLHOOKSTRUCT;*/
//128keypress 129 keyrelease
if (ks->flags == 128 || ks->flags == 129) {
if (ks->vkCode > 0x2F && ks->vkCode < 0x70) {
//0: 不传给下一个钩子 1:使按键无效
return 0;
}
}
//传给下个钩子
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
HookManager::HookManager(QObject* parent)
:QObject(parent) {
worker_ = new HookWorker;
worker_->moveToThread(&work_thread_);
connect(this, &HookManager::Work, worker_, &HookWorker::DoWork, Qt::QueuedConnection);
}
HookManager::~HookManager() {
StopKook();
if (worker_) {
worker_->deleteLater();
worker_ = nullptr;
}
}
void HookManager::StartHook() {
if (work_thread_.isRunning()) {
return;
}
work_thread_.start();
worker_->SetState(false);
emit Work();
return;
}
void HookManager::StopKook() {
worker_->SetState(true);
work_thread_.quit();
work_thread_.wait();
}
HookWorker::HookWorker() {
stop_ = false;
}
HookWorker::~HookWorker() {
}
void HookWorker::DoWork() {
MSG msg;
QTime hook_time = QTime::currentTime();
HHOOK hander = Hook();
while (!stop_) {
//处理消息,否则程序会卡死
if (PeekMessageA(&msg, NULL, NULL, NULL, PM_REMOVE)) {
// 把按键消息传递给字符消息
TranslateMessage(&msg);
// 将消息分派给窗口程序
DispatchMessageW(&msg);
}
else {
//避免CPU全负载运行
QThread::msleep(1);
}
if (hook_time.msecsTo(QTime::currentTime()) >= 500) {
hook_time = QTime::currentTime();
UnHook(hander);
hander = Hook();
}
}
}
HHOOK HookWorker::Hook() {
// 钩子类型,WH_KEYBOARD_LL 为键盘钩子
HHOOK keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL,
LowLevelKeyboardProc, GetModuleHandleA(NULL), NULL);// Dll 句柄
if (keyboard_hook == 0) {
LOG_WARN("挂钩键盘失败");
}
return keyboard_hook;
}
void HookWorker::UnHook(const HHOOK& hander) {
UnhookWindowsHookEx(hander);
}
2、使用RawInputData 从输入设备获取数据
参考代码,目前的想法是,通过key_event发送部分假按键信号,在内存中维护真实的字符串,这样外部程序获取到的数据也是有真有假的。(QQ实现不是这样的,都是假的键盘输出,详情)
3、GetAsyncKeyState轮询键盘消息
while (1) {
short a = ::GetAsyncKeyState(VK_LSHIFT);
if (0 != a) {
printf("0x%x", a);
}
Sleep(10);
}
未解决
要想彻底解决键盘监控需要在驱动层,利用IAT HOOK KeUserModeCallback的方法来防止消息钩子注入。
参考:键盘分析记录