33 lines
2.8 KiB
Markdown
33 lines
2.8 KiB
Markdown
### 单步执行的原理是什么
|
||
|
||
**1. 陷阱标志**
|
||
|
||
在 x86 架构的 CPU 中,有一个特殊的寄存器叫做 **EFLAGS**(在 64 位系统中是 RFLAGS)。这个寄存器中的每一位都代表一个特定的状态或控制标志。其中的第 8 位就是**陷阱标志(TF)**
|
||
|
||
- **当 TF=0 时**:CPU 正常执行指令,不会触发单步中断
|
||
- **当 TF=1 时**:这是单步执行的关键。CPU 在执行完一条指令后,会自动产生一个 **INT 1** 异常(也就是**单步中断**)
|
||
|
||
**2. 单步执行的原理流程**
|
||
|
||
当你在调试器中点击“单步”按钮时,幕后会发生以下几个步骤:
|
||
|
||
1. **调试器设置 TF 标志位:** 调试器通过系统调用或直接操作,将 **EFLAGS 寄存器中的 TF 位设置为 1**
|
||
2. **CPU 执行下一条指令:** CPU 继续正常执行程序代码中的下一条指令
|
||
3. **CPU 产生 INT 1 异常:** 在这条指令执行完毕后,CPU 检查到 TF 标志位为 1,于是自动停止正常的程序执行流程,并产生一个 **INT 1 异常**
|
||
4. **操作系统捕获异常:** 操作系统有一个专门的**中断描述符表(IDT)**。当 INT 1 异常发生时,操作系统会根据 IDT 中预先设置好的入口点,将控制权交给处理 INT 1 异常的程序
|
||
5. **调试器接管控制权:** 由于调试器是操作系统中管理被调试进程的组件,它会事先向操作系统注册一个异常处理函数。因此,当 INT 1 异常发生时,**控制权实际上被交给了调试器**
|
||
6. **调试器暂停程序:** 调试器在接管控制权后,会暂停被调试程序的执行,并显示当前的程序状态(寄存器值、内存数据等)
|
||
7. **等待用户操作:** 此时,程序在调试器中处于暂停状态,等待你进行下一步操作,比如再次单步、查看变量或继续运行
|
||
|
||
如果你再次点击“单步”,这个循环会重新开始:调试器再次设置 TF,程序执行一条指令,然后控制权再次回到调试器
|
||
|
||
**3. 特殊情况:`INT 3` 断点**
|
||
|
||
除了利用 TF 标志位,调试器还有一个常用的单步执行辅助手段,那就是**`INT 3` 指令**
|
||
|
||
- 当你在某行代码上设置了一个断点(Breakpoint)时,调试器会偷偷地将该行代码的第一个字节替换成 **`0xCC`**
|
||
- `0xCC` 对应的汇编指令就是 **`INT 3`**
|
||
- 当程序执行到这个位置时,CPU会像处理单步中断一样,产生一个 **INT 3 异常**
|
||
- 操作系统将控制权交给调试器,程序暂停,从而实现了“断点”的功能
|
||
|
||
所以,当调试器在一个断点处暂停后,你点击单步,调试器会先**将 `0xCC` 恢复为原来的指令**,然后**设置 TF 标志位**,让程序执行那条被恢复的指令,最后在指令执行完毕后,再次设置 `0xCC`,等待下一次的断点触发 |