软件逆向05

二进制插桩

Posted by orgaworl on September 10, 2024

1. 二进制插桩

不能移动原代码


2. 静态插桩SBI

使用二进制重写的方法永久修改磁盘上二进制程序。SBI对二进制程序进行反汇编,按需添加插桩代码并保存更新后程序。

要求:在不破坏现有代码和数据引用的前提下,添加插桩代码

优点

  • 速度快
  • 只需要发布修改后二进制程序即可 缺点
  • SBI插桩后造成的影响是永久的
  • SBI相较于DBI容易出错,因为需要先进行反汇编才可以插桩

指令替换法

将原指令替换为跳转指令,跳转到插桩代码

过程

  • 将插桩代码存放在独立位置
  • 当程序执行到插桩点时将控制流转移到插桩代码,执行:
    • 前置插桩代码
    • 被替换原指令
    • 后置插桩代码
  • 跳回插桩点之后的指令,恢复正常执行

缺点

  • jmp指令占用多个字节,无法对短指令进行插桩

int3方法

0x03号中断:调试器暂时替换正在运行的程序中的指令以设置代码断点。

int3操作码为0xcc,指令长度1字节,可覆盖任意指令

过程

  • 用int3指令覆盖原指令
  • SIGTRAP信号产生时,使用Linux提供的ptrace API 查找中断产生的地址
  • 根据插桩点位置调用对应插桩代码
  • 返回原程序

缺点

  • 软中断速度慢,运行开销大
  • 与使用int作为断点进行调试的程序不兼容

    跳板方法

    不直接对原始代码进行插桩 (但会进行修改),创建一个副本并对其进行插桩 不破坏任何代码和数据引用

主要解决间接控制流

流程

  • 创建所有原始函数副本,并放在新代码段中
  • 将原始代码中每个函数替换为 jmp指令+junk bytes
  • 每当有调用或跳转指令将控制流转移到原始代码中,该位置的跳板立即跳转到相应的插桩代码。

attention

  • 新插入的代码会导致代码移位,SBI引擎需要修补所有相对跳转指令的偏移
  • SBI引擎需要替换所有2字节跳转指令(8位偏移),换为5B长指令(32bit偏移),因为代码移位可能会导致偏移超过8bit
  • 重写直接调用,使其指向已插桩函数

总结 直接控制流

  • 相对跳转:需要SBI引擎修补
  • 绝对跳转:跳板
  • 直接调用:重写指向已插桩函数 间接控制流
  • 间接跳转:跳板
  • 间接调用:跳板
  • 跳转表:需要SBI引擎修改
    • 将跳转表的值修改为新地址
    • 在原始代码中的每个switch分支下放置一个跳板

1. switch跳转表实现 跳转到特定分支时,会间接跳转到原始代码,此处没有跳板会发生错误 尝试解决

  • 修改跳转表
    • 基本符号信息不包含switch语句布局信息
  • 在原始代码每个switch分支下放置一个跳板
    • 可能没有足够空间存放跳板

2. 内联数据 代码与数据混合时,跳板可能会覆盖部分内联数据

当反汇编错误时,任何改变都有可能破坏二进制程序

综上,跳板方法容易出错


3. 动态插桩DBI

监视二进制程序执行状态,在运行时将新指令插入指令流中,

优点

  • 避免了代码重定位问题
  • 不会破坏引用
  • 更容易使用,会监视程序和库的指令
  • DBI可以动态附加到进程,也可以从进程中分离,不影响程序正常使用
  • DBI更不易出错 缺点
  • 运行时插桩计算成本高,速度慢
  • 需要同时发布 二进制程序 和 包含插桩代码的DBI平台和工具

原理 在代码缓存中运行

  • DBI引擎从进程中提取一段代码,并按照DBI工具的指示对代码进行插桩
  • 插桩后使用JIT编译器编译代码,并将编译后代码存储在代码缓存中
  • 代码在缓存中执行,直到控制流指令要求获取新代码/在缓存中查找另一个代码块 过程中JIT编译器会控制流指令,以防止控制流转移到未插桩应用程序进程中执行 DBI引擎会模拟而不是直接运行某些指令

4. 对抗插桩

对抗动态调试

  • 基于代码缓存
  • 基于环境特征:插桩产生的指纹
  • 基于JIT编译
  • 其他反编译技术:运行开销、不支持指令