提问人:vengy 提问时间:9/18/2023 最后编辑:vengy 更新时间:9/18/2023 访问量:107
如何在启用陷阱标志时创建 INT 1 中断处理程序?
How to create an INT 1 interrupt handler when the Trap Flag is enabled?
问:
下面的 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 寄存器。FLAGS 寄存器是 x86 上的状态寄存器 包含控制或描述的各种位标志的处理器 处理器的状态。如果 Trap Flag 设置为 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 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
未设置 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
评论
从评论部分,有人注意到
如果在调试器下运行,则调试器将获取跟踪 中断。这是程序用作反调试器的一个技巧 技术。
添加了这个 C 程序来演示该技巧:
#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;
}
答:
2赞
vengy
9/18/2023
#1
添加了向量异常处理 (VEH) 以捕获 INT 1。
程序现在正常完成。
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
输出显示 VEH 在设置时被调用TF
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
使用以下命令构建
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
评论