拦截INT 15H/4FH实现Ctrl+Alt+S/H热键控制的问题咨询
INT 15H/4FH键盘钩子问题排查与修正
问题背景
需求是按下Ctrl+Alt+S时将boolDisplay变量设为1,按下Ctrl+Alt+H时设为0,通过拦截INT 15H的4FH服务实现键盘钩子,但按热键后无反应,同时有两个疑问:
- 拦截INT15H后是否需等待其再次触发才会生效?
- 如何正确拦截4FH服务?
用户提供的代码如下:
; Input Handling ; get-vector safe keep orignal int 15 mov ax, es:[15 * 4] mov word ptr oldInt15, ax mov ax, es:[15 * 4 + 2] mov word ptr oldInt15[2], ax ; Decide wether to call keyboard hook cmp ah, 4Fh je _KeyboardHook _KeyboardHook: in al, 60h cmp al, 16h je _set cmp al, 0bh je _clear _set: mov ax, 40h mov es, ax mov ax, es:[0017] and ax, 00110000B jz _dontSet mov boolDisplay, 01h _dontSet: jmp _Further _clear: mov ax, 40h mov es, ax mov ax, es:[0017] and ax, 00110000B jz _dontClear mov boolDisplay, 00h _dontClear: jmp _Further
代码核心问题分析
中断向量保存逻辑错误
直接读取es:[15*4]获取INT15向量是错误的——ES寄存器未被设置为中断向量表所在的0段,因此读取的是错误内存地址的值,保存的旧中断向量无效。正确做法是使用INT 35H获取中断向量:mov ah, 35h mov al, 15h int 21h ; ES:BX = 原INT15中断向量 mov word ptr oldInt15, bx mov word ptr oldInt15+2, es钩子未注册到中断向量表
代码仅保存了原向量,但未将自己的处理程序设置为INT15的新中断向量,导致钩子从未被触发。需要用INT 25H注册:mov ah, 25h mov al, 15h mov dx, offset MyInt15Handler mov ds, cs ; 若处理程序在代码段,需将DS指向CS int 21h热键状态检测完全错误
代码中用and ax, 00110000B检测40:17H键盘状态字节,实际这个掩码对应的是Scroll Lock和Num Lock状态,而非Ctrl和Alt。正确的Ctrl+Alt检测掩码应为00001100B(对应Bit2=Ctrl、Bit3=Alt):and ax, 00001100B cmp ax, 00001100B ; 确保Ctrl和Alt同时按下 jne _dontSetINT15H处理逻辑缺失
- 当AH≠4FH时,代码未调用原INT15H处理程序,会导致系统功能异常;
- 进入
_KeyboardHook后,未调用原INT15H的4FH服务,也没有执行IRET返回中断,会导致系统崩溃; - 直接读取60H端口获取扫描码时机错误——INT15H/4FH被调用时,INT09H已经处理过按键,60H端口的内容可能不是当前按键的扫描码,应通过INT15H的参数获取(调用AH=4FH时,AL通常携带扫描码相关信息)。
疑问解答
拦截INT15H后是否需等待再次触发才生效?
是的。中断处理程序只有在对应的中断被触发时才会执行。但前提是你已经正确将自己的处理程序注册到中断向量表中,否则永远不会被调用。如何正确拦截4FH服务?
正确流程如下:- 用
INT 35H获取原INT15H的中断向量并保存; - 用
INT 25H将自定义处理程序设置为INT15H的新向量; - 在自定义处理程序中:
- 先检查AH是否为4FH,若是则执行热键检测逻辑;
- 无论是否是4FH服务,都要调用原INT15H处理程序(保证系统原有功能正常);
- 最后用
IRET指令返回中断。
- 用
修正后的代码示例
; 定义全局变量 oldInt15 dd 0 ; 保存原INT15中断向量 boolDisplay db 0 ; 目标标志变量 ; 安装钩子 InstallHook: ; 获取原INT15向量 mov ah, 35h mov al, 15h int 21h mov word ptr oldInt15, bx mov word ptr oldInt15+2, es ; 设置新INT15向量为自定义处理程序 mov ah, 25h mov al, 15h mov dx, offset MyInt15Handler push cs pop ds int 21h ret ; 自定义INT15处理程序 MyInt15Handler: pushf cmp ah, 4Fh jne CallOldInt15 ; 非4FH服务,直接调用原中断 ; 处理4FH服务:检测热键 push ax bx es mov ax, 40h mov es, ax mov ax, es:[17h] ; 读取键盘状态字节 and ax, 00001100B ; 检测Ctrl+Alt是否同时按下 cmp ax, 00001100B jne SkipHotkeyCheck ; 获取扫描码(INT15H/4FH调用时AL通常为扫描码) cmp al, 16h ; S键的通码 je SetFlag1 cmp al, 0bh ; H键的通码 je SetFlag0 SkipHotkeyCheck: pop es bx ax CallOldInt15: ; 调用原INT15处理程序 jmp dword ptr [cs:oldInt15] SetFlag1: mov byte ptr [cs:boolDisplay], 1 jmp SkipHotkeyCheck SetFlag0: mov byte ptr [cs:boolDisplay], 0 jmp SkipHotkeyCheck
内容的提问来源于stack exchange,提问作者MUHAMMAD AWAIS
相关产品推荐
相关产品推荐

