The MASM program below enables the Trap Flag (TF) but it causes the program to exit prematurely
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
In order to enable single stepping of a processor, the x86 architecture provides a single bit Trap Flag (TF) that can be set in the FLAGS register. The FLAGS register is a status register on x86 processors that contains various bit-flags that control or describe the state of the processor. If the Trap Flag is set to true then the processor will make a call to interrupt 1 (INT 1) after each instruction is executed.
; +------+--------+-------------+----------------------------------------+
; | 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 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 ; 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
When the TF is not set, the program outputs
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
When the TF is set, the program outputs
Bit # 1, Value = 1
Bit # 2, Value = 1
Bit # 4, Value = 1
Bit # 6, Value = 1
Bit # 7, Value = 1
Interestingly, stepping thru the program with Windbg, the program tries to set TF=1, but Windbg prevents it?
Question
Is there an easy way to implement an INT 1 handler, so the program can catch it?
Maybe something like this
INT1handler PROC
; ...
; ...
; ...
iretd ; Return from interrupt handler
INT1handler ENDP
Comments
From the comments section, it was noted
If you are running under a debugger, the debugger gets the trace interrupt. This is one trick programs use as an anti-debugger technique.
Added this C program to demo that trick:
#include <stdio.h>
#include <windows.h>
int main() {
BOOL isDebugged = TRUE;
__try
{
__asm
{
pushfd
or dword ptr[esp], 0x100 // Set the Trap Flag TF=1
popfd // Load the value into EFLAGS register
nop // Single-step exception (INT 01h) generated after executing the nop.
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// If an exception has been raised – debugger is not present
isDebugged = FALSE;
}
if (isDebugged)
{
printf("Debugger Present!");
exit(-1);
}
return 0;
}
Added Vectored Exception Handling (VEH) to catch the INT 1.
The program now completes normally.
VEH.asm
; +------+--------+-------------+----------------------------------------+
; | 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
EXTERN printf:proc
EXTERN AddVectoredExceptionHandler:proc
EXTERN RemoveVectoredExceptionHandler: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
EXCEPTION_MAXIMUM_PARAMETERS equ 15
EXCEPTION_CONTINUE_EXECUTION equ -1
EXCEPTION_CONTINUE_SEARCH equ 0
EXCEPTION_SINGLE_STEP equ 080000004h
; Struct for exception record
EXCEPTION_RECORD STRUCT 8
ExceptionCode DWORD ?
ExceptionFlags DWORD ?
ExceptionRecord QWORD ?
ExceptionAddress QWORD ?
NumberParameters DWORD ?
__unusedAlignment DWORD ?
ExceptionInformation QWORD EXCEPTION_MAXIMUM_PARAMETERS DUP(?)
EXCEPTION_RECORD ENDS
; Contains processor-specific register data
CONTEXT STRUCT
P1Home QWORD ?
P2Home QWORD ?
P3Home QWORD ?
P4Home QWORD ?
P5Home QWORD ?
P6Home QWORD ?
ContextFlags DWORD ?
MxCsr DWORD ?
SegCs WORD ?
SegDs WORD ?
SegEs WORD ?
SegFs WORD ?
SegGs WORD ?
SegSs WORD ?
EFlags DWORD ?
; ...
CONTEXT ENDS
EXCEPTION_POINTERS STRUCT
ExceptionRecord QWORD ?
ContextRecord QWORD ?
EXCEPTION_POINTERS ENDS
.data
strCF db "Bit # %d, Value = %d",13,10,0
message DB "Single step exception captured!",13,10
DB "ExceptionCode: %X",13,10
DB "EFlags: %X",13,10,0
.data?
hHandler QWORD ?
.code
VectoredHandler PROC
; Save non-volatile registers
PUSH RBX
PUSH RBP
PUSH RDI
PUSH RSI
PUSH R12
PUSH R13
PUSH R14
PUSH R15
; RCX holds a pointer to the EXCEPTION_POINTERS structure
MOV RSI, [RCX + EXCEPTION_POINTERS.ExceptionRecord]
MOV RDI, [RCX + EXCEPTION_POINTERS.ContextRecord]
CMP DWORD PTR [RSI + EXCEPTION_RECORD.ExceptionCode], EXCEPTION_SINGLE_STEP
JNE notOurException
; Handle the exception
NUM_ARGS = 3
PUSH RSP
PUSH [RSP]
ALIGN_STACK NUM_ARGS
MOV R8D, [RDI + CONTEXT.EFlags]
MOV EDX, DWORD PTR [RSI + EXCEPTION_RECORD.ExceptionCode]
LEA RCX, message
SUB RSP, 32
CALL printf
RESTORE_STACK NUM_ARGS
POP RSP
; Clear the TF flag in the EFlags/RFlags field of the CONTEXT (0FEFFh = 1111111011111111)
AND DWORD PTR [RDI + CONTEXT.EFlags], 0FEFFh
MOV RAX, EXCEPTION_CONTINUE_EXECUTION
JMP outta_here
notOurException:
MOV RAX, EXCEPTION_CONTINUE_SEARCH
outta_here:
; Restore non-volatile registers
POP R15
POP R14
POP R13
POP R12
POP RSI
POP RDI
POP RBP
POP RBX
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
The output shows the VEH is called when TF
is set
Bit # 1, Value = 1
Bit # 2, Value = 1
Bit # 4, Value = 1
Bit # 6, Value = 1
Bit # 7, Value = 1
Single step exception captured!
ExceptionCode: 80000004
EFlags: 206
Bit # 9, Value = 1
Bit # 10, Value = 1
Bit # 11, Value = 1
Built using these commands
if not defined DevEnvDir (
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat"
)
ml64.exe VEH.asm /link /subsystem:console /defaultlib:kernel32.lib /defaultlib:libcmt.lib