下面的 MASM 程序启用了陷阱标志(TF),但它会导致程序过早退出
pushf ; Push FLAGS onto the stack
pop ax ; Pop FLAGS into AX register
or ax, 0100h ; Set the Trap Flag bit 0100h = 0000000100000000
push ax ; Push the modified value onto the stack
popf ; Pop it back into FLAGS
为了启用处理器的单步执行,x86 架构提供了一位陷阱标志(TF),可以在 标志寄存器。 FLAGS 寄存器是 x86 上的状态寄存器 包含控制或描述的各种位标志的处理器 处理器的状态。如果陷阱标志设置为 true,则 处理器将在每次执行后调用中断 1 (INT 1) 指令被执行。
; +------+--------+-------------+----------------------------------------+
; | Bit #| Mask | Abbreviation| Description |
; +------+--------+-------------+----------------------------------------+
; | 0 | 0x0001 | CF | Carry flag |
; | 1 | 0x0002 | — | Reserved |
; | 2 | 0x0004 | PF | Parity flag |
; | 3 | 0x0008 | — | Reserved |
; | 4 | 0x0010 | AF | Adjust flag |
; | 5 | 0x0020 | — | Reserved |
; | 6 | 0x0040 | ZF | Zero flag |
; | 7 | 0x0080 | SF | Sign flag |
; | 8 | 0x0100 | TF | Trap flag |
; | 9 | 0x0200 | IF | Interrupt flag |
; | 10 | 0x0400 | DF | Direction flag |
; | 11 | 0x0800 | OF | Overflow flag |
; |12-13 | 0x3000 | IOPL | I/O privilege level (286+ only), |
; | | | | always all-1s on 8086 and 186 |
; | 14 | 0x4000 | NT | Nested task flag (286+ only), |
; | | | | always 1 on 8086 and 186 |
; | 15 | 0x8000 | MD | Mode flag (NEC V-series only), |
; | | | | reserved on all Intel CPUs. |
; | | | | Always 1 on 8086/186, 0 on 286 and |
; | | | | later. |
; +------+--------+-------------+----------------------------------------+
option casemap:none
extrn printf:proc
ALIGN_STACK MACRO num_args
IF (num_args AND 1) EQ 0 OR num_args LT 5
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 ; Push RFLAGS onto the stack
pop r8 ; Load the RFLAGS from the stack into R8
mov cl, al ; Move the lower 8 bits of RAX into CL
shr r8, cl ; Shift right by CL, so the flags bit is now the LSB of R8
and r8, 1 ; Zero all other bits, except the LSB
;; call the printf function
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
; Carry flag Bit #1
; -----------------
mov ax, 0FFFFh ; 0FFFFh + 1 = 010000h, but since AX is 16 bits,
add ax, 1 ; it wraps around the most significant bit into CF
mov eax, 1 ; Bit #1
call PrintFlagsBit
; Parity flag Bit #2
; ------------------
mov al, 00110011b ; This has 4 set bits - Even Parity
test al, al ; TEST sets the PF = 1 (even parity) based on the value in AL
mov eax, 2 ; Bit #2
call PrintFlagsBit
; Adjust flag Bit #4
; ------------------
mov al, 0Fh ; 0000 1111 + 0000 0001 = 0001 0000
add al, 01h ; Addition carried 3rd bit to 4th bit
mov eax, 4 ; Bit #4
call PrintFlagsBit
; Zero flag Bit #6
; -----------------
mov eax, 5 ; Load EAX register with the value 5
sub eax, 5 ; Subtract 5 from EAX, result is zero, so ZF is set
mov eax, 6 ; Bit #6
call PrintFlagsBit
; Sign flag Bit #7
; -----------------
mov eax, 1 ; Load EAX register with the value 1
neg eax ; Negate the value in EAX, most significant bit is 1, so SF is set
mov eax, 7 ; Bit #7
call PrintFlagsBit
; Trap flag Bit #8
; ----------------
; Set TF flag
pushf ; Push FLAGS onto the stack
pop ax ; Pop FLAGS into AX register
or ax, 0100h ; Set the Trap Flag bit 0100h = 0000000100000000
push ax ; Push the modified value onto the stack
popf ; Pop it back into FLAGS
mov eax, 8 ; Bit #8
call PrintFlagsBit
; Unset TF flag
pushf ; Push FLAGS onto the stack
pop ax ; Pop FLAGS into AX register
and ax, 0FEFFh ; Unset the Trap Flag bit 0FEFFh = 1111111011111111
push ax ; Push the modified value onto the stack
popf ; Pop it back into FLAGS
; Interrupt flag Bit #9
; ---------------------
;sti ; Set Interrupt Flag (already set)
;cli ; Clear Interrupt Flag
mov eax, 9 ; Bit #9
call PrintFlagsBit
; Direction flag Bit #10
; ----------------------
std ; sets the DF flag, ensuring backward operations.
mov eax, 10 ; Bit #10
call PrintFlagsBit
cld ; clears the DF flag, ensuring forward operations.
; Overflow flag Bit #11
; ---------------------
mov al, 07Fh ; AL = 01111111 (unsigned 127, MSB 0)
add al, 01h ; AL = 10000000 (signed -128, MSB 1)
mov eax, 11 ; Bit #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 ; Return from interrupt handler
INT1handler ENDP
添加了向量异常处理以捕获 INT 1。
程序现已正常完成。
; +------+--------+-------------+----------------------------------------+
; | Bit #| Mask | Abbreviation| Description |
; +------+--------+-------------+----------------------------------------+
; | 0 | 0x0001 | CF | Carry flag |
; | 1 | 0x0002 | — | Reserved |
; | 2 | 0x0004 | PF | Parity flag |
; | 3 | 0x0008 | — | Reserved |
; | 4 | 0x0010 | AF | Adjust flag |
; | 5 | 0x0020 | — | Reserved |
; | 6 | 0x0040 | ZF | Zero flag |
; | 7 | 0x0080 | SF | Sign flag |
; | 8 | 0x0100 | TF | Trap flag |
; | 9 | 0x0200 | IF | Interrupt flag |
; | 10 | 0x0400 | DF | Direction flag |
; | 11 | 0x0800 | OF | Overflow flag |
; |12-13 | 0x3000 | IOPL | I/O privilege level (286+ only), |
; | | | | always all-1s on 8086 and 186 |
; | 14 | 0x4000 | NT | Nested task flag (286+ only), |
; | | | | always 1 on 8086 and 186 |
; | 15 | 0x8000 | MD | Mode flag (NEC V-series only), |
; | | | | reserved on all Intel CPUs. |
; | | | | Always 1 on 8086/186, 0 on 286 and |
; | | | | later. |
; +------+--------+-------------+----------------------------------------+
option casemap:none
extrn printf:proc
extrn AddVectoredExceptionHandler:proc
extrn RemoveVectoredExceptionHandler:proc
ALIGN_STACK MACRO num_args
IF (num_args AND 1) EQ 0 OR num_args LT 5
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
EXCEPTION_MAXIMUM_PARAMETERS equ 15
EXCEPTION_CONTINUE_EXECUTION equ -1
EXCEPTION_CONTINUE_SEARCH equ 0
EXCEPTION_SINGLE_STEP equ 080000004h
EXCEPTION_RECORD STRUCT 8
ExceptionCode DWORD ?
ExceptionFlags DWORD ?
ExceptionRecord DWORD ?
ExceptionAddress DWORD ?
NumberParameters DWORD ?
__unusedAlignment DWORD ?
ExceptionInformation QWORD EXCEPTION_MAXIMUM_PARAMETERS DUP(?)
EXCEPTION_RECORD ENDS
.data
strCF db "Bit # %d, Value = %d",13,10,0
message db "Single step exception captured! ExceptionCode: %X",13,10,0
.DATA?
exceptionPointers QWORD EXCEPTION_POINTERS ?
hHandler QWORD ?
.code
VectoredHandler PROC
; First, obtain the EXCEPTION_RECORD pointer from EXCEPTION_POINTERS
MOV R8, [RCX] ; Now, R8 has the EXCEPTION_RECORD pointer
MOV EBX, DWORD PTR [R8 + EXCEPTION_RECORD.ExceptionCode]
cmp EBX, EXCEPTION_SINGLE_STEP
jne notOurException
; Handle the exception here.
mov rax, 8 ; Bit #8 (TF)
call PrintFlagsBit
; Call the printf function
NUM_ARGS = 2
PUSH RSP
PUSH [RSP]
ALIGN_STACK NUM_ARGS
MOV RDX, RBX
LEA RCX,message
SUB RSP,32
CALL printf
RESTORE_STACK NUM_ARGS
POP RSP
pushfq ; Push FLAGS onto the stack
and qword ptr [rsp], 0FEFFh ; Unset the Trap Flag bit 0FEFFh = 1111111011111111
popfq ; Pop it back into FLAGS
mov rax, EXCEPTION_CONTINUE_EXECUTION
ret
notOurException:
mov rax, EXCEPTION_CONTINUE_SEARCH
ret
VectoredHandler ENDP
PrintFlagsBit:
pushfq ; Push RFLAGS onto the stack
pop r8 ; Load the RFLAGS from the stack into R8
mov cl, al ; Move the lower 8 bits of RAX into CL
shr r8, cl ; Shift right by CL, so the flags bit is now the LSB of R8
and r8, 1 ; Zero all other bits, except the LSB
;; call the printf function
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
NUM_ARGS = 2
PUSH RSP
PUSH [RSP]
ALIGN_STACK NUM_ARGS
LEA RDX, VectoredHandler
MOV RCX,1
SUB RSP,32
CALL AddVectoredExceptionHandler
RESTORE_STACK NUM_ARGS
POP RSP
test rax, rax
jz errorExit
mov [hHandler], rax
; Carry flag Bit #1
; -----------------
mov ax, 0FFFFh ; 0FFFFh + 1 = 010000h, but since AX is 16 bits,
add ax, 1 ; it wraps around the most significant bit into CF
mov eax, 1 ; Bit #1
call PrintFlagsBit
; Parity flag Bit #2
; ------------------
mov al, 00110011b ; This has 4 set bits - Even Parity
test al, al ; TEST sets the PF = 1 (even parity) based on the value in AL
mov eax, 2 ; Bit #2
call PrintFlagsBit
; Adjust flag Bit #4
; ------------------
mov al, 0Fh ; 0000 1111 + 0000 0001 = 0001 0000
add al, 01h ; Addition carried 3rd bit to 4th bit
mov eax, 4 ; Bit #4
call PrintFlagsBit
; Zero flag Bit #6
; -----------------
mov eax, 5 ; Load EAX register with the value 5
sub eax, 5 ; Subtract 5 from EAX, result is zero, so ZF is set
mov eax, 6 ; Bit #6
call PrintFlagsBit
; Sign flag Bit #7
; -----------------
mov eax, 1 ; Load EAX register with the value 1
neg eax ; Negate the value in EAX, most significant bit is 1, so SF is set
mov eax, 7 ; Bit #7
call PrintFlagsBit
; Trap flag Bit #8
; ----------------
pushfq ; Push FLAGS onto the stack
or qword ptr [rsp], 100h ; Set the Trap Flag bit 0100h = 0000000100000000
popfq ; Pop it back into FLAGS
; Interrupt flag Bit #9
; ---------------------
;sti ; Set Interrupt Flag (already set)
;cli ; Clear Interrupt Flag
mov eax, 9 ; Bit #9
call PrintFlagsBit
; Direction flag Bit #10
; ----------------------
std ; sets the DF flag, ensuring backward operations.
mov eax, 10 ; Bit #10
call PrintFlagsBit
cld ; clears the DF flag, ensuring forward operations.
; Overflow flag Bit #11
; ---------------------
mov al, 07Fh ; AL = 01111111 (unsigned 127, MSB 0)
add al, 01h ; AL = 10000000 (signed -128, MSB 1)
mov eax, 11 ; Bit #11
call PrintFlagsBit
NUM_ARGS = 1
PUSH RSP
PUSH [RSP]
ALIGN_STACK NUM_ARGS
MOV RCX,[hHandler]
SUB RSP,32
CALL RemoveVectoredExceptionHandler
RESTORE_STACK NUM_ARGS
POP RSP
errorExit:
RET
main endp
end
现在输出
Bit # 1, Value = 1
Bit # 2, Value = 1
Bit # 4, Value = 1
Bit # 6, Value = 1
Bit # 7, Value = 1
Bit # 8, Value = 0
Single step exception captured! ExceptionCode: 80000004
Bit # 9, Value = 1
Bit # 10, Value = 1
Bit # 11, Value = 1