x86架构 指令INT3只有一个字节的原因

news2024/12/23 17:18:04

文章目录

  • 一、单字节原因简介
  • 二、断点原理
  • 三、单字节具体原因
  • 参考资料

一、单字节原因简介

在这里插入图片描述
INT3指令生成一个特殊的单字节操作码(CC),用于调用调试异常处理程序。(这种单字节形式很有价值,因为它可以用来用断点替换任何指令的第一个字节,包括其他单字节指令,而不会重写其他代码)。

软件断点指令应该是最小的指令大小,这样它就不会覆盖可能成为跳转目标的指令,并且当程序跳到断点指令的中间时会导致灾难。(严格来说,断点必须不大于可能成为跳转目标的指令之间的最小间隔

二、断点原理

必须首先了解调试器是如何在程序中插入断点的。下面是gdb如何实现断点。

(1)当我们在gdb中键入“break OFFSET”时,其中OFFSET是一个指令地址,gdb将存储在OFFSET的字节的值存储起来,并将该字节的值设置为INT3(0xcc)。

假设OFFSET处的原始指令是0x8345fc01(addl $0x1,-0x8(%ebp))。gdb将记住该字的最后一个字节(0x01),并将该字更改为(0x8345fccc),这可能不是真正的指令。但这并不重要,正如我们将在第3步中看到的那样:

OFFSET    01    # original 
OFFSET+1  fc 
OFFSET+2  45
OFFSET+3  83 

OFFSET    cc    # breakpoint inserted
OFFSET+1  fc 
OFFSET+2  45
OFFSET+3  83 

(2)接下来,假设我们继续调试程序。当该程序命中调试器刚刚插入的INT3指令时,被调试的程序将陷入内核,而内核将反过来向GDB程序发出信号,告诉被调试的程序已经进入了断点调试位置。

(3)gdb将使用它存储的原始值恢复OFFSET处的字节,并将指令指针EIP(RIP)移回OFFSET以在OFFSET处重新启动指令。它需要移动EIP(RIP),EIP(RIP)指令指针保存的地址需要减1,因为CPU执行INT3指令后,EIP(RIP)已经增加了1。

使用相同的示例,调试器将用原始指令0x8345fc01替换可能无效的指令0x8345 fccc,并将EIP(RIP)设置为OFFSET。

OFFSET    cc   # after breakpoint    
OFFSET+1  fc   # <---- EIP points here
OFFSET+2  45
OFFSET+3  83 

OFFSET    01    # <--- EIP points here, after gdb restores instruction and EIP
OFFSET+1  fc 
OFFSET+2  45
OFFSET+3  83 

这里给出一个简单的演示示例:

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <unistd.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

//设置断点,将断点处的第一条指令改为 0xcc
void setbp(pid_t pid, void *addr, long* orig)
{
    union {
            long word;
            unsigned char bytes[4];
    }u;
    //保存断点处的原始指令
    *orig = u.word = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
    u.bytes[0] = 0xcc; // 0xcc is INT3
    ptrace(PTRACE_POKETEXT, pid, addr, u.word);
    printf("set breakpoint (0x%lx) at 0x%lx.\n", 
            u.word, (unsigned long)addr);
}

//恢复断点
void unsetbp(pid_t pid, void *addr, long orig)
{
    printf("remove breakpoint at 0x%lx.\n", 
            (unsigned long)addr);
    ptrace(PTRACE_POKETEXT, pid, addr, orig);

    struct user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, 0, &regs);

#if __WORDSIZE == 64
    regs.rip = (long)addr;
#else
    regs.eip = (long)addr;
#endif
    ptrace(PTRACE_SETREGS, pid, 0, &regs);

}

void breakpoint(pid_t pid, void* addr)
{
    long orig;

    while(1) {
        setbp(pid, addr, &orig);

        printf("executing...\n");
        ptrace(PTRACE_CONT, pid, 0, 0);

        wait(NULL);
        printf("breakpoint hit. press return to continue\n");
        getchar();
        unsetbp(pid, addr, orig);
        
        // single step to next instruction, so we can set
        // breakpoint again
        ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
        wait(NULL);
    }
        
}

int main(int ac, char *av[])
{
    if(ac == 2) { // test loop, executed by child process.
        int i = 0;
        while(1) {
            printf ("debugee: %d\n", i++);
        bp_addr:
            sleep(2);
        }
        return 0;
    }

    int pid;
    switch(pid=fork()) {

    case -1: 
        perror("fork"); 
        break;

    //子进程
    case 0: 
        ptrace(PTRACE_TRACEME, 0, 0, 0);
        // exec myself, but with one argument
        execlp(av[0], av[0], "loop", NULL);
        break;

    //父进程
    default: 
        wait(NULL);
        ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC);
        breakpoint(pid, &&bp_addr); 
        break;
    }
    return 0;
}

sys/user.h 文件有一个注释,该文件只是用于GDB:

此文件的全部目的仅用于GDB。不要读太多。除非你知道自己在做什么,否则不要把它用于GDB之外的任何事情。

// sys/user.h

#ifdef __x86_64__

struct user_regs_struct
{
  __extension__ unsigned long long int r15;
  __extension__ unsigned long long int r14;
  __extension__ unsigned long long int r13;
  __extension__ unsigned long long int r12;
  __extension__ unsigned long long int rbp;
  __extension__ unsigned long long int rbx;
  __extension__ unsigned long long int r11;
  __extension__ unsigned long long int r10;
  __extension__ unsigned long long int r9;
  __extension__ unsigned long long int r8;
  __extension__ unsigned long long int rax;
  __extension__ unsigned long long int rcx;
  __extension__ unsigned long long int rdx;
  __extension__ unsigned long long int rsi;
  __extension__ unsigned long long int rdi;
  __extension__ unsigned long long int orig_rax;
  __extension__ unsigned long long int rip;
  __extension__ unsigned long long int cs;
  __extension__ unsigned long long int eflags;
  __extension__ unsigned long long int rsp;
  __extension__ unsigned long long int ss;
  __extension__ unsigned long long int fs_base;
  __extension__ unsigned long long int gs_base;
  __extension__ unsigned long long int ds;
  __extension__ unsigned long long int es;
  __extension__ unsigned long long int fs;
  __extension__ unsigned long long int gs;
};

三、单字节具体原因

现在,让我们看看为什么INT3应该是一个单字节指令,即所有x86指令的最小长度。假设INT3比某些x86指令长。当我们使用上面的方法插入断点时,我们可能会覆盖多个指令,这可能会导致问题。考虑以下带有两个单字节指令的示例:

OFFSET     <instruction 1, one byte>
OFFSET+1   <instruction 2, one byte>

假设我们想在指令1处设置一个断点。为此,我们必须用INT3覆盖指令1和指令2:

OFFSET     <INT3...................
OFFSET+1    ......................>

在大多数情况下,这将是很好的,因为我们可以在OFFSET命中INT3之后恢复这两个指令。然而,如果某些代码想要跳转到“OFFSET+1”,我们将遇到麻烦;它实际上会跳到INT3指令的中间,它可以创建未定义的行为。如果INT3是一个字节,即所有x86指令的最小长度,我们就不会有这个问题。

为什么调试器要覆盖指令以插入断点?难道他们不能插入一个断点并将所有后续指令移位一个字节吗?这样做很复杂,因为它会干扰指令的偏移量,并导致跳转指令跳到错误的目标。重复使用上面的例子,如果我们在指令1之前插入断点,我们将把原始代码转换为:

OFFSET     INT3
OFFSET+1   <instruction 1>
OFFSET+2   <instruction 2>

如果某个代码想跳到原始代码中OFFSET+1处的指令2,它将跳到转换后的代码中的指令1,从而计算错误的结果。

参考资料

https://www.cs.columbia.edu/~junfeng/09sp-w4118/lectures/int3/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/948806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

冠达管理:火爆!拼多多飙涨15%,中概股沸腾!这些外资巨头唱多中国资产

当地时间8月29日&#xff0c;美国三大股指团体收涨&#xff0c;道指涨0.85%&#xff0c;标普500指数涨1.45%&#xff0c;纳指涨1.74%。科技股大涨&#xff0c;特斯拉涨7.69%&#xff0c;英伟达涨4.16%。纳斯达克我国金龙指数收涨3.7%&#xff0c;拼多多涨超15%。 广东研山私募…

开源百度电商小程序源码 含完整代码包+安装部署教程 一键搭建商城小程序

分享一款开源百度电商小程序源码&#xff0c;含完整代码包安装部署教程&#xff0c;一键搭建商城小程序&#xff0c;源码开源可二开&#xff0c;已测试完美运营版&#xff0c;帮你一键搭建百度商城小程序&#xff0c;含多套模板、自由DIY功能和完整的搭建部署教程。程序支持除百…

微信测试号实现微信分享等功能

目录 1 申请微信测试号 2 测试号信息 3 下载微信的测试代码 4 将下载的代码放到自己的服务器上 5 接口配置信息 6 JS安全域名 ​7 扫码关注&#xff0c;只有关注的微信号才能测试 ​8 修改sample.php文件 9 在微信上打开&#xff0c;并分享 10 问题 1 申请微信测试号 …

【USRP】调制解调系列1:AM、FM解调

AM&#xff0c;DSB&#xff0c;SSB和VSB的联系是都为幅度调制&#xff08;调幅&#xff09; 区别&#xff1a; AM是调幅&#xff0c;带载波。 DSB是抑制载波的调幅&#xff0c;可增加功率效率&#xff0c;但两个边带均传输相同的信息。 SSB单边带抑制了一个边带&#xff0c…

高忆管理:etf联接基金有哪些?

ETF联接基金自面世以来&#xff0c;就备受出资者重视&#xff0c;由于它可以以比较低的本钱获得和国际市场相同的收益&#xff0c;而且具备杰出的流动性和简略易懂的买卖方法。那么&#xff0c;ETF联接基金都有哪些呢&#xff1f; 一、什么是ETF联接基金&#xff1f; ETF联接基…

Vue怎么安装?看这一篇就够了!

Vue2.0 安装 一般我们都不会单独用 npm 去安装 Vue 插件&#xff0c;而是通过脚手架 Vue CLI 去初始化一个 Vue 项目。 Vue CLI 安装 ::: warning 注意 使用默认的镜像源安装 npm 第三方包可能要很长时间&#xff0c;建议你已经替换了新的镜像源。如需要请查看 npm 镜像源修…

不用订阅,不用破解,永久免费使用Axure最新版教程

首先去官网下载最新的axure&#xff0c;你没听错&#xff0c;就是最新的。 下载网址&#xff1a;Axure RP - UX Prototypes, Specifications, and Diagrams in One Tool 下载完后解压安装到本地&#xff0c;并注册属于你自己的账户&#xff0c;开始试用。可惜的是只有30天的试…

零基础如何备考通过PMP?

​01为什么要去考&#xff1f;新手能考吗&#xff1f; 首先PO一个官方解释&#xff1a; PMP——项目管理专业人士资格认证&#xff0c;由项目管理协会&#xff08;Project Management Institute&#xff09;发起&#xff0c;在全球206多个国家和地区得到高度认可。 通俗点说就…

如何自己实现一个丝滑的流程图绘制工具(七)bpmn-js 批量删除、复制节点

背景 希望实现批量删除和复制节点&#xff0c;因为bpmn-js是canvas画的&#xff0c;所以不能像平时页面上的复制一样直接选择范围&#xff0c;会变成移动画布。 思路是&#xff1a; 绘制一个选择的效果框&#xff0c;这样才可以看出来选的节点有哪些。 上面的选中范围框效果…

CSS 实现平面圆点绕椭圆动画

前言 &#x1f44f;CSS实现平面圆点绕椭圆动画,速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现原理 transform-style&#xff1a;CSS 属性 transform-style 设置元素的子元素是位于 3D 空间中还是平面中。如果选择平面&#xf…

Python GUI应用程序开发之wxPython使用详解

概要 wxPython是一个强大的跨平台GUI工具包&#xff0c;它使用Python编程语言开发&#xff0c;提供了丰富的控件功能。如果你是一名Python开发者&#xff0c;而且希望创建一个功能齐全的桌面应用程序&#xff0c;那么wxPython是一个值得考虑的选择。 什么是wxPython wxPython…

【论文阅读】你看不见我:对基于激光雷达的自动驾驶汽车驾驶框架的物理移除攻击

文章目录 AbstractIntroduction Abstract 自动驾驶汽车(AVs)越来越多地使用基于激光雷达的物体检测系统来感知道路上的其他车辆和行人。目前&#xff0c;针对基于激光雷达的自动驾驶架构的攻击主要集中在降低自动驾驶物体检测模型的置信度&#xff0c;以诱导障碍物误检测&…

宏观经济和风电预测误差分析(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【LeetCode-中等题】199. 二叉树的右视图

文章目录 题目方法一&#xff1a;层序遍历取每一层最后一个元素方法二&#xff1a;深度优先搜索 题目 方法一&#xff1a;层序遍历取每一层最后一个元素 // 方法一 &#xff1a;层序 集合(取每层子集合最后一个元素)// List<List<Integer>> Rlist new ArrayList…

运动耳机哪款最好、热门运动耳机推荐

对于当代年轻人而言&#xff0c;运动健身已成为最流行的压力释放方式。然而&#xff0c;我们不得不承认&#xff0c;运动本身确实是一项沉闷而艰苦的挑战&#xff0c;需要我们付出大量汗水和持之以恒的坚持。 在运动过程中&#xff0c;许多人会借助音乐的律动来激励自己不断向前…

「2024」预备研究生mem-数学进阶卷(二)8.27 比较易错的难题,存在特殊方法

一共12道题&#xff1a; 回忆&#xff1a; 不错&#xff1a; 男生 没有要求顺序 &#xff0c;就不用A55了

得帆CTO徐翔轩:AIGC X 低代码,解锁无限可能

AIGC与低代码 在Open AI的ChatGPT的带动下&#xff0c;2023年已经成为公认的AI之年&#xff0c;Chat-GPT、Notion AI、Copilot等杀手级AI应用更是让大家认知到了AIGC的能力与潜力。AIGC在创新、效率等方面带来的颠覆性&#xff0c;让越来越多的数字化从业者感受到冲击与期待&a…

Android OTA 相关工具(六) 使用 lpmake 打包生成 super.img

我在 《Android 动态分区详解(二) 核心模块和相关工具介绍》 介绍过 lpmake 工具&#xff0c;这款工具用于将多个分区镜像打包生成一个 Android 专用的动态分区镜像&#xff0c;一般称为 super.img。Android 编译时&#xff0c;系统会自动调用 lpmake 并传入相关参数来生成 sup…

傅里叶变换(FFT)笔记存档

参考博客&#xff1a;https://www.luogu.com.cn/blog/command-block/fft-xue-xi-bi-ji 目录&#xff1a; FFT引入复数相关知识单位根及其相关性质DFT过程&#xff08;难点&#xff09;DFT结论&#xff08;重要&#xff09;IDFT结论&#xff08;重要&#xff09;IDFT结论证明&…