content_views"
class="htmledit_views">
如果在执行指令时c;处理器检测到 EFLAGS 寄存器中的 TF 标志被设置c;则会生成单步调试异常。该异常属于陷阱类异常c;因为异常是在指令执行之后生成的。处理器不会在设置 TF 标志的指令之后立即生成此异常。例如c;如果使用 POPF 指令设置 TF 标志c;则单步陷阱不会发生c;直到 POPF 指令color:#fe2c24">后面的指令执行之后。
c="https://i-blog.csdnimg.cn/direct/face0b8968d5412393052c324577c056.png" width="980" />
c="https://i-blog.csdnimg.cn/direct/bea7401d402c4c109d9ddbb3bc2dc159.png" width="1360" />
c="https://i-blog.csdnimg.cn/direct/c4490f9e02c240eab57ffebb42a2cf5e.png" width="1328" />
来跟踪一下class="tags" href="/tags/WINDOWS.html" title=windows>windows的TF异常
- 查看1号向量的处理函数
c="https://i-blog.csdnimg.cn/direct/30de7d281fc34ac28e8d7815f66a7627.png" width="771" /> - 在IDA中断到处理程序,可以看到处理器已经帮我们在栈中压入了参数
c="https://i-blog.csdnimg.cn/direct/217536cde99942abbcebf2298660d411.png" width="1249" /> - 如果是3环的单步调试,class="tags" href="/tags/WINDOWS.html" title=windows>windows切换了栈,rsp从KPCR.Prcb.RspBase 获得
并复制了CPU压入的值
c="https://i-blog.csdnimg.cn/direct/0b28c78847d34381b473b9e6e7f187eb.png" width="1087" /> - 进入KxDebugTrapOrFault函数,它清掉了栈上的TF标志,看调试器如何处理,如果没有人处理,那么执行IRET执行后,不会继续执行单步
c="https://i-blog.csdnimg.cn/direct/3172b40e8154415aaced9f33fa29f476.png" width="886" /> - 可以看到EFLAGS寄存器从原来的0x306h变成了0x206H ,清除了[8]TF位
c="https://i-blog.csdnimg.cn/direct/b889f7d3aed2427989bd26611676a01e.png" width="842" /> - 我们使用P命令,单步执行,按照逻辑,windbg应该要修改这个地址,并重新设置TF标志位.所以在windbg中使用写时中断指令, 希望在写这个地址的时候中断下来,看看是怎么处理的
c="https://i-blog.csdnimg.cn/direct/2ff82ebe398c482296e0a00a9a3b4f25.png" width="1763" /> - 这里可以看到,如果要继续执行使用单步调试, 除了重新设置上TF标志位时,,class="tags" href="/tags/WINDOWS.html" title=windows>windows还设置了RF标志位.这是为什么呢?
c="https://i-blog.csdnimg.cn/direct/ae6a4ae6d7c742808cd906dd0e14b555.png" width="897" />
RF标志位的作用
RF标志的主要功能是允许在指令断点条件导致的调试异常后重新启动指令。在这里c;调试软件必须在堆栈上的EFLAGS映像中设置此标志c;然后才能使用IRETD返回中断的程序(以防止指令断点导致另一个调试异常)。然后c;在返回的指令成功执行后c;处理器会自动清除此标志c;再次启用指令断点故障。
另请参见:第18.3.1.1节c;“指令断点异常条件”
-----来自因特尔白皮书 Vol. 3A 2-11
由于指令断点的调试异常在指令执行前触发c;若异常处理程序未移除断点c;处理器在重启指令时会再次检测到断点并触发异常。为避免死循环c;Intel 架构通过 EFLAGS 寄存器中的 RF 标志(Resume Flag) 控制:当 <code>RF=1code> 时c;处理器忽略指令断点。
调试异常处理程序可通过设置栈中 EFLAGS 镜像的 RF 标志c;避免指令断点重复触发。当通过 <code>IRETD/IRETQcode> 或任务切换返回时c;栈中的 <code>RFcode> 值会被复制到 EFLAGS 寄存器c;使处理器忽略下一条指令的断点。
ckquote>
这里强调的是 instruction-breakpoint (指令断点,在英特尔白皮书中 instruction-breakpoint 指的是使用DR寄存器的情况)
ckquote>
所以邓志的书中有误导,将RF和TF标志位一起介绍,这是错误的,RF标志位只是为了防止指令断点的重入,因为他是fault类型的异常,可以理解为:指令执行前,CPU的检查到有指令断点.
c="https://i-blog.csdnimg.cn/direct/fd61193385f041ec8ba8396247c79c99.png" width="927" />
如果没有设置RF标志位,CPU执行前检查,发现符合指令断点条件,那么将导致循环触发指令断点
class="image">
c="https://i-blog.csdnimg.cn/direct/9ccf08feaf824764825a92ad94eefdf7.png" width="902" />
caption>
不设置RF标志
caption>
class="image">
c="https://i-blog.csdnimg.cn/direct/c7d296e07d1e4704b583e4fba7e9b784.png" width="840" />
caption>
设置RF标志
caption>
RF 标志的管理规则:
-
清除时机:在指令开始执行时(检查指令断点、代码段限制违规和浮点异常后)c;RF 被清除。
-
上下文切换:任务切换和 <code>IRETD/IRETQcode> 指令会将 RF 从 TSS/栈复制到 EFLAGS 寄存器。
-
异常处理:
-
非指令断点的故障类异常:压入栈的 EFLAGS 镜像中 <code>RF=1code>。
-
指令断点调试异常:压入栈的 EFLAGS 镜像中 <code>RFcode> 保持原值。
-
中断或陷阱类异常:若发生在重复字符串指令的非最后一次迭代中c;<code>RF=1code>。
asm
复制
mov ecx, 3 ; 循环次数 ECX=3
lea esi, [src] ; 源地址
lea edi, [dest] ; 目标地址
rep movsb ; 重复复制字节:执行 3 次 MOVSB
场景 1:陷阱类异常发生在非最后一次迭代
-
迭代过程:
-
第 1 次迭代(ECX=3 → ECX=2):正常执行 <code>MOVSBcode>。
-
第 2 次迭代(ECX=2 → ECX=1):执行 <code>MOVSBcode> 时触发 陷阱类异常(如除零异常)。
-
处理器行为:
-
压入栈的 EFLAGS 镜像中 <code>RF=1code>(因异常发生在非最后一次迭代)。
-
异常处理程序返回后c;处理器继续执行 第 3 次迭代(ECX=1 → ECX=0)。
-
由于 <code>RF=1code>c;第 3 次迭代的 <code>MOVSBcode> 不会触发指令断点(即使存在断点)。
场景 2:陷阱类异常发生在最后一次迭代
-
迭代过程:
-
第 3 次迭代(ECX=1 → ECX=0):执行 <code>MOVSBcode> 时触发陷阱类异常。
-
处理器行为: