在 x86 上以保护模式触发中断时,是否可以确定触发的中断号是多少?例如,假设我调用了 int 0xFF。在处理程序中,我可以发现调用了 int 0xFF 吗?
3 回答
如果您有唯一的中断处理程序(或者,至少有唯一的入口点和序言代码),那么当然,您可以int 0xFF
区别于int 0x30
. ISR 地址存储在 中IDT
,因此,这就是区分开始的地方。
除了独特的 ISR,没有很好的替代方案。这就是为什么...
在 ISR 中,您可以检查调用者的堆栈、查看返回地址并检查返回地址之前的代码,以查看它是否是 2 字节int n
指令(编码为字节:0xCD,n)或其他内容。int 3
问题是,还有into
1 字节指令(分别编码为 0xCC 和 0xCE)。您如何区分 0xCD+0xCC ( int 0xCC
) 或 0xCD+0xCE ( int 0xCE
) 以及简单的 0xCC ( int 3
) 或 0xCE ( into
)?在 0xCC 或 0xCE 之前可能有任何东西。指令的可变长度不允许我们轻松可靠地向后分析/反汇编代码。
其他触发中断/异常的方式呢,比如ud2
?还是触发#GP、#PF 的指令?这些可以是任意指令。
此外,您应该记住,异常不是以完全相同的方式处理的。其中一些带有CPU在进入ISR之前在堆栈上保存的额外信息,它是错误代码。其他人没有此错误代码,您的 ISR 需要在执行之前将其删除iret
。确定异常向量的错误会导致代码崩溃或挂起。
现在,关于硬件中断......您可能能够确定正在服务的硬件中断。有PIC
( ),其中位设置为 1 (AFAIR) 表示in-service register
,但是如果您让较高优先级的中断抢占 ISR 处理较低优先级的中断(通过启用 ISR 内部的中断),那么中断识别很快就会变得更加复杂比必要的。ISR
IRQ
因此,只需为所有 IRQ、异常和系统调用使用唯一的 ISR。或者使用一个常见的 ISR,但具有多个唯一入口点,每个入口点在堆栈上保存一个唯一编号(=向量编号)。之后的通用代码将提取此数字并执行该中断向量所需的操作。
制作将值推送到堆栈的存根是处理此问题的一种巧妙方法,并且在James Molloy 教程中使用:
%macro ISR_NOERRCODE 1
global isr%1
isr%1:
cli ; Disable interrupts firstly.
push byte 0 ; Push a dummy error code.
push byte %1 ; Push the interrupt number.
jmp isr_common_stub ; Go to our common handler code.
%endmacro
%macro ISR_ERRCODE 1
global isr%1
isr%1:
cli ; Disable interrupts.
push byte %1 ; Push the interrupt number
jmp isr_common_stub
%endmacro
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE 8
ISR_NOERRCODE 9
ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14
ISR_NOERRCODE 15
/* More entries */
这也处理错误代码。
我不是中断处理程序方面的专家,但它们必须在某个地方有一个返回地址,以便原始代码可以恢复执行。如果您可以使用旧地址,那么您可以检查以前的地址,该地址可能包含中断号。