You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

启用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寄存器标志位详情

位号掩码缩写描述
00x0001CF进位标志
10x0002—保留
20x0004PF奇偶标志
30x0008—保留
40x0010AF调整标志
50x0020—保留
60x0040ZF零标志
70x0080SF符号标志
80x0100TF陷阱标志(单步执行)
90x0200IF中断允许标志
100x0400DF方向标志
110x0800OF溢出标志
12-130x3000IOPLI/O特权级(仅286及以上),在8086和186上始终为全1
140x4000NT嵌套任务标志(仅286及以上),在8086和186上始终为1
150x8000MD模式标志(仅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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:30:58