启用Trap Flag后程序提前退出,如何实现INT 1中断处理程序?
Trap Flag(TF)设置导致程序提前退出的解决与INT 1处理程序实现
以下MASM代码尝试设置Trap Flag(TF),但会导致程序提前退出:
pushf ; 将FLAGS寄存器压栈 pop ax ; 从栈中弹出FLAGS到AX寄存器 or ax, 0100h ; 设置TF位(0100h对应第8位) push ax ; 将修改后的值压栈 popf ; 弹回FLAGS寄存器
为实现处理器单步执行,x86架构在FLAGS寄存器中提供了单比特的Trap Flag(TF)。FLAGS是处理器的状态寄存器,包含控制和描述处理器状态的各类标志位。当TF设为1时,处理器会在每条指令执行后触发中断1(INT 1)。
FLAGS寄存器标志位详情
| 位号 | 掩码 | 缩写 | 描述 |
|---|---|---|---|
| 0 | 0x0001 | CF | 进位标志 |
| 1 | 0x0002 | — | 保留 |
| 2 | 0x0004 | PF | 奇偶标志 |
| 3 | 0x0008 | — | 保留 |
| 4 | 0x0010 | AF | 调整标志 |
| 5 | 0x0020 | — | 保留 |
| 6 | 0x0040 | ZF | 零标志 |
| 7 | 0x0080 | SF | 符号标志 |
| 8 | 0x0100 | TF | 陷阱标志(单步执行) |
| 9 | 0x0200 | IF | 中断允许标志 |
| 10 | 0x0400 | DF | 方向标志 |
| 11 | 0x0800 | OF | 溢出标志 |
| 12-13 | 0x3000 | IOPL | I/O特权级(仅286及以上),在8086和186上始终为全1 |
| 14 | 0x4000 | NT | 嵌套任务标志(仅286及以上),在8086和186上始终为1 |
| 15 | 0x8000 | MD | 模式标志(仅NEC V系列),Intel CPU保留;8086/186上始终为1,286及以上为0 |
完整MASM测试程序
option casemap:none extrn printf:proc ALIGN_STACK MACRO num_args IF num_args LT 5 OR (num_args AND 1) EQ 0 AND SPL, 0F0h ELSE OR SPL, 08h ENDIF ENDM RESTORE_STACK MACRO num_args IF num_args LT 5 LEA RSP, [RSP + 5*8] ELSEIF (num_args AND 1) EQ 0 LEA RSP, [RSP + (num_args + 1)*8] ELSE LEA RSP, [RSP + num_args*8] ENDIF ENDM .data strCF db "Bit # %d, Value = %d",13,10,0 .code PrintFlagsBit: pushfq ; 将RFLAGS压栈 pop r8 ; 从栈中加载RFLAGS到R8 mov cl, al ; 将RAX低8位移动到CL shr r8, cl ; 右移CL位,目标标志位变为R8的最低位 and r8, 1 ; 清零其他位,仅保留最低位 ;; 调用printf函数 NUM_ARGS = 3 PUSH RSP PUSH [RSP] ALIGN_STACK NUM_ARGS MOV RDX, RAX LEA RCX,strCF SUB RSP,32 CALL printf RESTORE_STACK NUM_ARGS POP RSP ret main proc ; 进位标志 位#1 ; ----------------- mov ax, 0FFFFh ; 0FFFFh + 1 = 010000h,但AX是16位, add ax, 1 ; 最高位溢出到CF mov eax, 1 ; 位#1 call PrintFlagsBit ; 奇偶标志 位#2 ; ------------------ mov al, 00110011b ; 有4个置位的比特 - 偶校验 test al, al ; TEST指令根据AL的值设置PF=1(偶校验) mov eax, 2 ; 位#2 call PrintFlagsBit ; 调整标志 位#4 ; ------------------ mov al, 0Fh ; 0000 1111 + 0000 0001 = 0001 0000 add al, 01h ; 加法从第3位进位到第4位 mov eax, 4 ; 位#4 call PrintFlagsBit ; 零标志 位#6 ; ----------------- mov eax, 5 ; 将EAX加载为5 sub eax, 5 ; EAX减5,结果为0,ZF置位 mov eax, 6 ; 位#6 call PrintFlagsBit ; 符号标志 位#7 ; ----------------- mov eax, 1 ; 将EAX加载为1 neg eax ; 取反EAX,最高位为1,SF置位 mov eax, 7 ; 位#7 call PrintFlagsBit ; 陷阱标志 位#8 ; ---------------- ; 设置TF标志 pushf ; 将FLAGS压栈 pop ax ; 弹出FLAGS到AX or ax, 0100h ; 设置TF位(0100h = 0000000100000000) push ax ; 将修改后的值压栈 popf ; 弹回FLAGS mov eax, 8 ; 位#8 call PrintFlagsBit ; 取消TF标志 pushf ; 将FLAGS压栈 pop ax ; 弹出FLAGS到AX and ax, 0FEFFh ; 清除TF位(0FEFFh = 1111111011111111) push ax ; 将修改后的值压栈 popf ; 弹回FLAGS ; 中断允许标志 位#9 ; --------------------- ;sti ; 设置中断允许标志(默认已开启) ;cli ; 清除中断允许标志 mov eax, 9 ; 位#9 call PrintFlagsBit ; 方向标志 位#10 ; ---------------------- std ; 设置DF标志,确保反向操作 mov eax, 10 ; 位#10 call PrintFlagsBit cld ; 清除DF标志,确保正向操作 ; 溢出标志 位#11 ; --------------------- mov al, 07Fh ; AL = 01111111(无符号127,最高位0) add al, 01h ; AL = 10000000(有符号-128,最高位1) mov eax, 11 ; 位#11 call PrintFlagsBit RET main endp end
程序输出对比
未设置TF时程序输出
Bit # 1, Value = 1 Bit # 2, Value = 1 Bit # 4, Value = 1 Bit # 6, Value = 1 Bit # 7, Value = 1 Bit # 8, Value = 0 Bit # 9, Value = 1 Bit # 10, Value = 1 Bit # 11, Value = 1
设置TF后程序输出
Bit # 1, Value = 1 Bit # 2, Value = 1 Bit # 4, Value = 1 Bit # 6, Value = 1 Bit # 7, Value = 1
调试器拦截现象
有意思的是,用Windbg单步调试时,程序尝试设置TF=1,但Windbg会阻止该操作。
核心问题
有没有简便的方法实现INT 1处理程序,让程序能够捕获该中断?示例框架如下:
INT1handler PROC ; ... ; 自定义处理逻辑 ; ... iretd ; 从中断处理程序返回 INT1handler ENDP
备注与反调试技巧
若程序在调试器下运行,调试器会捕获跟踪中断。这是程序常用的反调试技巧之一。
以下C程序演示了该反调试逻辑:
#include <stdio.h> #include <windows.h> int main() { BOOL isDebugged = TRUE; __try { __asm { pushfd or dword ptr[esp], 0x100 // 设置Trap Flag TF=1 popfd // 将值加载到EFLAGS寄存器 nop // 执行nop后会触发单步异常(INT 01h) } } __except (EXCEPTION_EXECUTE_HANDLER) { // 如果触发异常,说明没有调试器 isDebugged = FALSE; } if (isDebugged) { printf("调试器已存在!"); exit(-1); } return 0; }
内容的提问来源于stack exchange,提问作者vengy
相关产品推荐
相关产品推荐

