关注、星标公众号,直达精彩内容

来源:CSDN | maomao171314

整理:技术让梦想更伟大 | 李肖遥

软件断点

INT 3 指令,即通常所说的“软件断点”,一条X86系列处理器专门用于支持调试的指令。该指令目的是使CPU中断(break)到调试器,供调试器对执行现场进行各种分析。

INT 3

Visual C++ 嵌入内联汇编指令,示例如下:

VS没有下断点,程序会自动断在INT 3 指令所在的位置。这正是通过注入代码手工设置断点的方法。

反汇编窗口如下:

内存地址002719CE 处有INT 3 指令。

打开寄存器窗口,EIP=002719CE

INT 3属于陷阱异常,当CPU产生异常时,EIP指向的是导致异常的下一条指令。但是EIP指向的是导致异常的指令——为什么会发生回跳?

断点命中

当CPU执行INT 3指令时,在执行异常处理例程之前,CPU会保存当前的执行上下文。

实模式下INT 3 指令的执行过程:

1 REAL-ADDRESS-MODE:2 IF ((vector_number ∗ 4) + 3) is not within IDT limit  //检查根据向量号计算出向量地址是否超出了边界3 THEN #GP;//发生保护性错误异常4 FI;//IF语句的结束语句5 IF stack not large enough for a 6-byte return information //检查栈是否有空间保存寄存器6 THEN #SS;//堆栈不足以保存要压入的6字节内容(CS、IP和EFLAGS的低16位),产生堆栈异常7 FI;//IF语句的结束语句8 Push (EFLAGS[15:0]);9 IF ← 0; (* Clear interrupt flag *) //清除IF10 TF ← 0; (* Clear trap flag *) //清除TF11 AC ← 0; (* Clear AC flag *) //清除AC12 Push(CS); //保存当前段寄存器13 Push(IP); //保存程序指针寄存器14 (* No error codes are pushed *)15 CS ← IDT(Descriptor (vector_number ∗ 4), selector));  //将异常处理例程入口地址加载到CS和IP寄存器16 EIP ← IDT(Descriptor (vector_number ∗ 4), offset)); (* 16 bit offset AND 17 0000FFFFH *)

在实模式的单任务操作系统,CPU直接执行调试器注册的断点异常处理例程。然后执行中断返回指令(IRET),恢复被调试程序,从断点位置继续执行。

保护模式下的INT 3指令的执行流程原理上与实模式一致。

Windows保护模式下的多任务操作系统,INT 3 异常的处理函数是内核函数KiTrap03。断点指令在用户模式下的应用程序代码中,CPU会从用户模式转入内核模式。经过几个内核函数分发和处理。由于这个异常是来自用户模式,且该异常的拥有进程正在被调试(进程的Debug Port不为0),所以内核例程会把这个异常通过调试子系统以调试事件的形式分发给用户模式的调试器,内核的调试子系统会等待调试器的回复,收到调试器的回复后,调试子系统会返回到异常处理例程,异常处理例程执行IRET指令使被调试程序回复执行。

在调试器收到调式事件后,会在内部寻找与其匹配的断点记录。如果能找到,则允许用户进行交互式调试。如果找不到,则说明该断点是程序内置的断点,会弹出异常。

在Windows中,操作系统的断点异常处理函数对于x86 CPU的断点异常会有一个特殊的处理:将EIP的值减1。出于这个原因,我们在调试器看到的程序指针指向的仍然是INT 3指令的位置,而不是它的下一条指令。这样处理的目的是:

  1. 调试器在落实断点时只替换一个字节,如果程序指针发生改变指向了下一条指令的位置,指向的可能是原来多字节指令的第二个字节,不是一条完整的指令,造成程序的错误。

  2. 由于断点的存在,被调试程序于断点位置的指令在断点触发时还未被执行,按照“程序指针总是指向将要执行的那条指令”的原则,应该让其指向原指令,即倒退一个字节,指向原指令起始位置。

至此,回跳的问题得到了解答。

恢复执行

当用户结束分析希望恢复被调试程序执行时,调试器通过调试API通知调试子系统,这会使系统内核的异常分发函数返回到异常处理例程,然后异常处理例程通过IRET/IRETD指令触发一个异常返回动作,使CPU恢复执行上下文,从发生异常的位置继续执行。

当断点命中中断到调试器时,调试器会把所有断点处的INT 3替换成原本的内容,因此当用户发出恢复执行的命令后,调试器在通知系统真正恢复程序的执行前需要将断点列表所有断点全部落实一遍,但是对于命中的断点需要特殊处理——如果落实了命中断点,那么程序一恢复执行便会再次触发断点;如果没有落实,程序下次执行到该部分便不会中断。对于这种情况,大多数调试器的做法都是先单步执行一次,设置单步执行标志,然后恢复执行,将断点所在位置的指令执行完。由于设置了单步标志,CPU执行完断点位置的这条指令后会再次中断到调试器中,这次调试器不会通知用户,而是做一些内部操作后恢复程序的执行,而且将所有断点落实,这一过程一般称为“单步走出断点”,如果用户在恢复程序执行前取消了该断点,就不需要单步执行一次。

INT 3指令的特殊用途

由于INT 3 指令的特殊性,对应的机器码是0xCC,对应的汉字是“烫”。编译器在编译调试版本时会用0xCC填充刚刚分配的缓冲区,就是下图经常见到的情形:

编译器还用INT 3 指令来填充函数或代码段末尾的空闲区域,即用它来做内存对齐。

断点API

用户模式,使用DebugBreak() API ,内核模式下使用DbgBreakPoint() 或DbgBreakPointWithStatus() API 主动插入断点。

DebugBreak() 反汇编如下,只是对INT 3指令的简单包装:

1 lkd> u nt!DbgBreakPoint2 nt!DbgBreakPoint:3 804df8c4 cc int 34 804df8c5 c3 ret

DbgBreakPointWithStatus()允许向调试器传递一个整型参数:

lkd> u nt!DbgBreakPointWithStatus804df8d1 8b442404 mov eax,[esp+0x4]804df8d5 cc int 3

其中[esp+0x4]代表DbgBreakPointWithStatus函数的第一个参数。

0xCD03

INT 3指令与当n=3时的INT n指令不同,INT n指令对应的机器码是0xCD后跟1字节的n值,比如INT 23H会被编译为0xCD23。与此不同的是,INT 3指令具有独特的单字节机器码0xCC。用户可以通过_EMIT伪指令来直接嵌入机器码。

#include<stdlib.h>int main(){// 手工断点_asm INT 3;printf("Hello INT 3!\n");_asm{mov eax, eax__asm _emit 0xcd __asm _emit 0x03nopnop}//或者使用Windows APIDebugBreak();return 0;}

C++程序在执行的过程中会中断到调试器,但是继续执行会报访问冲突错误。使用windbg打开可执行文件,在反编译窗口中发现了0xCD03指令

00421a9e cc                      int     300421a9f 68c47b4200      push   offset test!`string' (00427bc4)00421aa4 e89ef9ffff          call    test!ILT+1090(_printf) (00421447)00421aa9 83c404             add    esp,400421aac 8bc0                 mov    eax,eax00421aae cd03                int      300421ab0 90                   nop00421ab1 90                   nop00421ab2 8bf4                mov     esi,esp00421ab4 ff155cb04200    call      dword ptr [test!_imp__DebugBreak (0042b05c)]

反汇编程序将0xCD03翻译成了INT 3指令,继续执行,windbg会报以下错误

(c84.1e34): Break instruction exception - code 80000003 (first chance)eax=0000000d ebx=01058000 ecx=23648826 edx=79dc4a6c esi=004213e3 edi=00fef828eip=00421aaf esp=00fef75c ebp=00fef828 iopl=0         nv up ei pl nz na pe nccs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206test!main+0x2f:00421aaf 0390908bf4ff    add     edx,dword ptr [eax-0B7470h] ds:002b:fff48b9d=????????

其中80000003是系统定义的断点异常代码,此时程序的EIP=0X00421aaf,这指向的是位于0x00421aae的0xCD03指令的第二个字节。由于EIP指向的是一条指令的中间而不是起始处,后面的指令都错位了。以下为对比

#中断前的反汇编00421a9e cc                     int     300421a9f 68c47b4200      push   offset test!`string' (00427bc4)00421aa4 e89ef9ffff          call    test!ILT+1090(_printf) (00421447)00421aa9 83c404             add    esp,400421aac 8bc0                 mov    eax,eax00421aae cd03                int      300421ab0 90                    nop00421ab1 90                   nop00421ab2 8bf4                mov     esi,esp00421ab4 ff155cb04200    call      dword ptr [test!_imp__DebugBreak (0042b05c)]
#中断后的反汇编(c84.1e34): Access violation - code c0000005 (!!! second chance !!!)eax=0000000d ebx=01058000 ecx=23648826 edx=79dc4a6c esi=004213e3 edi=00fef828eip=00421aaf esp=00fef75c ebp=00fef828 iopl=0         nv up ei pl nz na pe nccs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206test!main+0x2f:00421aaf 0390908bf4ff    add     edx,dword ptr [eax-0B7470h] ds:002b:fff48b9d=????????

可以看到,中断后余下的指令都已变得面目全非。由于EIP总是指向将要执行的指令,因此程序会尝试访问eax-0B7470h的内存地址,该地址为非法,因此会导致访问失效错误。

导致该EIP错位的原因是KiTrap03在分发这个异常前总是会将EIP减1,对于单字节的INT 3指令,这样的减法过后刚好指向INT 3指令或原来指令的起始地址。但是对于双字节的0xCD03指令,执行后EIP指向的是该指令的第二个字节处。解决方法为在断点命中后手动修改EIP,重定向至原本的下一指令处,调整后程序可以继续执行。

0:000> r eip = eip+1# 修改eip后的反汇编KERNELBASE!DebugBreak+0x2:76aff092 cc                  int     376aff093 c3                  ret76aff094 8bff                mov     edi,edi76aff096 55                  push    ebp76aff097 8bec              mov     ebp,esp76aff099 68ffff0080      push    8000FFFFh76aff09e 6a03              push    376aff0a0 ff7504            push    dword ptr [ebp+4]

归纳与提示

软件断点具有以下局限性:

  • 属于代码类断点,适用于代码段,不使用于数据段和I/O空间

  • 对在ROM中执行的程序(如BIOS)无法动态加载软件断点

  • 在VDT或IDT还未准备就绪或被破坏的情况下,软件断点无法正常工作

原文链接:https://blog.csdn.net/maomao171314/article/details/109749352


版权归原作者所有,如有侵权,请联系删除。

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

关注我的微信公众号,回复“加群”按规则加入技术交流群。

欢迎关注我的视频号:

点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

嵌入式软件调试之软件断点相关推荐

  1. GDB再学习(6):断点调试之软件断点

    系列文章目录 GDB再学习(1):前言 GDB再学习(2):编译 GDB再学习(3):GDB的启动和运行 GDB再学习(4):程序准备 GDB再学习(5):常用指令介绍 GDB再学习(5.1):常用指 ...

  2. 软件调试学习笔记(五)—— 软件断点内存断点

    软件调试学习笔记(五)-- 软件断点&内存断点 调试的本质 软件断点 软件断点的执行流程 分析INT 3执行流程 实验:处理软件断点 内存断点 内存断点的执行流程 实验:处理内存断点 调试的本 ...

  3. 调试器工作原理--CPU软件断点/硬件断点/单步执行标识

    断点和单步执行是两个经常使用的调试功能,也是调试器的核心功能. 断点是调试器的最常用技术之一.其基本思想是在某一个位置设置一个陷阱,当CPU执行到此位置时,中断到调试器中,让调试者分析和调试,之后恢复 ...

  4. 软件调试中的断点分类

    软件调试中断点分类,主要分为以下类别 -- 1.int3软件断点 2.内存断点 3.硬件断点

  5. 调试笔记--keil 断点调试小技巧

    调试笔记–keil 断点调试小技巧 给变量打断点 调试不熟悉的项目时,卧槽!怎么这么多全局变量?这玩意又在那修改了??这个时候会给变量打断点就能省好多事. 将要监视的全局变量添加到watch窗口 选中 ...

  6. [系统安全] 二十四.逆向分析之OllyDbg调试INT3断点、反调试、硬件断点与内存断点

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

  7. 硬件断点和软件断点的区别

    硬断点--break point 软断点--assert 简单的解释: 硬件断点:硬断点需要硬件寄存器提供支持,断点的数目受Embedded ICE中的Watchpoint数目的限制,但是可以在任何地 ...

  8. 单片机编程软件很简单(14),Keil单片机编程软件断点设置

    单片机编程软件十分常用,对于单片机编程软件,小编于往期文章中做过诸多介绍.本文对于单片机编程软件的介绍基于Keil,主要内容在于介绍该单片机编程软件的在线汇编功能以及断点设置.如果你对Keil单片机编 ...

  9. 嵌入式软件调试:任务执行时间与负载率

    嵌入式软件调试:任务执行时间与负载率 1 基本概念及原理 1.1 负载率概念 1.2 时间统计方式 1.3 任务占用统计方式 2 功能函数执行时间测试 2.1 绝对执行时 2.2 相对执行时间(任务负 ...

最新文章

  1. 业界对生成图片缩略图的做法归纳
  2. 列表组件之ListView
  3. 《青春飞扬》诗集出版历程与思考分享 之三:游记、感悟与思考
  4. 「Swift」第三章String and Character
  5. 2022.4.9 mac os M1 芯片 12.3.1 Monterey 安装cocoapods
  6. matlab刘卫国课后答案第三版,MATLAB程序设计与应用(刘卫国编)课后实验答案
  7. python电商用户购买力分析_Python + pandas + 不同客户购买力图形显示
  8. 教师节PSD分层海报设计模板 | 最好的海报,送给最好的老师们
  9. magento网站建设_跨境自建站Magento麦进斗代打包代贴单代发货
  10. html退出登录_退出登录 0152
  11. Assetbundle coustomerScripts
  12. Google Code Review 浏览评论中的CL
  13. 电脑PC端实现微信多开
  14. linux下搭建redis集群
  15. Everything软件配置
  16. Vue一级二级三级域名下cookie值共享(不同域名cookie共享)
  17. 怎么注册tk域名_.TK后缀顶级域名的免费注册图文教程
  18. 国赛培训——最优化智能算法——模拟退火
  19. 嵌入式Linux--根文件系统(二)BusyBox构建根文件系统
  20. 四阶段课堂总结解决问题

热门文章

  1. 软件工程头歌人机交互部分设计用例
  2. matlab模拟三体运动_如何写出三体的MATLAB程序-理论分析篇
  3. linux的周期行计划任务叫做atd,linux基本命令之计划任务
  4. 海思3559万能平台搭建:OSD的自动反色
  5. python用matplotlib画玫瑰_Python可视化:用Matplotlib画个玫瑰图
  6. 三极管应用电路---低通滤波电路
  7. 树莓派与DS18B20温度传感器模块的使用
  8. ORACLE exp时出现1455错误,全网唯一正解,建议收藏
  9. 电商平台“二选一” 最后买单的却是商家和消费者
  10. 借力《旅行青蛙》,阿里手游便可叫板腾讯、网易?