0%

静态插桩

预备知识

一段代码,从 .c 源文件到可执行文件经历了4个步骤:

  • 预处理(Preprocessing):主要处理源代码中以#开始的预编译指令,比如#include#define 等等。预处理完得到的是.i文件。
  • 编译(Compilation):编译完成后,得到的是:汇编文件(.s
  • 汇编(Assembly):将汇编代码转换成机器可以执行的指令,每一条汇编指令都会对应一条机器指令。汇编完成后,得到的是:可重定向目标文件(.o)。
  • 链接(Linking):通常一份文件会调用其他文件中编写的函数,但当前文件怎么知道去哪里寻找它想调用的函数呢?这就需要链接。链接完成后,得到的是:可执行目标文件。

其中:可重定向目标文件、可执行目标文件都必须符合ELF格式(Executable and Linkable Format),因此也可称为:ELF文件


ELF文件包含一下三个部分:

  • ELF header
  • Sections
  • Section header table

下图是一个 Hello World 可执行程序的 ELF 格式实例。

ELF

我们可以通过命令行工具readelf查看 elf 文件。

1
$ readelf -h demo   #查看elf header

ELF header

1
$ readelf -S demo   #查看各个section信息

Sections


这里容易混淆的是:ELF HeaderProgram HeaderSection Header

  • ELF Header 是 ELF 文件的文件头,它是文件的起始部分,为加载器和链接器提供必要的信息来处理文件。
  • Program Header 是一个数据结构,它定义了程序如何加载到内存中。每个 Program Header 都包含了一个段(Segment)的加载信息,比如它的类型、在文件中的偏移量、在内存中的地址、大小等。
  • Program Header Table 是一个由多个 Program Header 组成的数组,它按照程序头的顺序排列。Program Header Table 通常位于 ELF Header 之后,通常在文件的开始部分,这样加载器可以快速地找到并读取它。
  • Section Header 定义了文件中的节(Section)的属性和信息。每个节头条目(Section Header Entry)都包含了关于一个特定节的元数据。

注意:

Sections 中的第一个 Section(索引为0的section)就是 Program Header Table

以之前图中 ELF 文件为例。

**ELF Header **占据最开始的64个字节。

紧接着是Program Header Table,总共占据 56 * 13 = 728个字节。

所以真正的第一个section(.interp)开始地址应该是:64 + 728 = 792 = 0x318。


另外一个容易混淆的是:节(Section)段(Segment)

  • Section 称为节,是指在汇编源码中经由关键字section或segment修饰、逻辑划分的指令或数据区域,汇编器会将这两个关键字修饰的区域在目标文件中编译成节,也就是说”节”最初诞生于目标文件中。

  • Segment 称为段,是,链接器把目标文件链接成可执行文件,因此段最终诞生于可执行文件中。我们平时所说的可执行程序内存空间中的代码段和数据段就是指的Segment。

Section Vs Segment

  • 左边是ELF的链接视图,可以理解为是目标文件的内容布局。
  • 右边是ELF的执行视图,可以理解为可执行文件的内容布局。

注意:目标代码文件的内容是由 Section 组成的,而可执行文件的内容是由 Segment 组成的。

  • 我们查看汇编程序时,.text.bss.data 都指的是 Section。目标代码文件中的 SectionSection Header Table 中的条目是一一对应的。Section 的信息用于链接器对代码重定位。

  • 而文件载入内存执行时,是以 Segment 组织的,每个 Segment 对应 ELF 文件中 Program Header Table 中的一个条目,用来建立可执行文件的进程映像。链接器将目标文件中属性相同的多个 Section 合并,从而形成新的可执行文件;合并后的 Section 的集合,称为 Segment。例如:所有.text Section 会被合并成一个新的.text Segment

注意:在目标文件中,Program Header 不是必须的,我们用 gcc 生成的目标文件也不包含 Program Header


在所有Sections中,有一个section比较特殊:.shstrtab

Section Header String Table

在之前查看 ELF Header 的时候,ELF Header 就提供了 .shstrtab Section 的索引是多少。

  • 节头字符串表(Section Header String Table,简称SHSTRTAB)是 ELF 文件中的一个特殊 section。它包含所有 section 名称的字符串。

  • 每个 Section Header 中的sh_name字段是一个索引,指向节头字符串表中的一个位置,该位置存储了该 section 的名称。

  • 节头字符串表本质上是一个以空字符(\0)分隔的字符串集合。每个section的名称在这个表中都有一个唯一的偏移量,该偏移量存储在对应section头部的sh_name字段中。

    1
    2
    3
    4
    0x00: "\0"
    0x01: ".text\0"
    0x07: ".data\0"
    0x0D: ".bss\0"

技术路线

我们希望采用跳板技术实现静态插桩。

  • 跳板技术:简单来说,就是通过设置一个中间点(跳板),让程序先跳到这个中间点,然后由这个中间点再跳到最终的目标函数去执行。这个过程就像是设置了一个中转站,让程序先到中转站,再由中转站引导到目的地。
  • 我们给目标程序新增加一个section作为跳板(trampoline)。

具体分为两个函数:

  1. 创造trampoline空间的函数:Crt_Trpline
  2. 进行二进制插桩的函数: Sinsert
  3. 复原函数:recover

代码实现

bfd.pdf

在BFD(Binary File Descriptor)库中,bfd_make_section 及其相关函数用于创建新的节(section)并将其附加到BFD(Binary File Descriptor)结构中。这些函数提供了不同的选项和功能,以适应不同的使用场景。以下是一些常见的 make section 函数及其区别和功能:

  1. bfd_make_section

    • 功能:创建一个新的空节,并将其附加到BFD的节链表中。
    • 参数:只需要提供BFD和节的名称。
    • 特点:如果同名节已存在,则不会创建新的节,而是返回现有的节。
  2. bfd_make_section_anyway

    • 功能:创建一个新的空节,即使同名节已存在,也会创建新的节,并附加到BFD的节链表中。
    • 参数:提供BFD和节的名称。
    • 特点:不检查是否已存在同名节,总是创建新的节。
  3. bfd_make_section_with_flags

    • 功能:创建一个新的节,并为其设置特定的标志(flags)。
    • 参数:提供BFD、节的名称和要设置的标志。
    • 特点:允许在创建节时指定节的属性,如是否可加载、是否可执行等。
  4. bfd_make_section_anyway_with_flags

    • 功能:创建一个新的节,并为其设置特定的标志,即使同名节已存在。
    • 参数:提供BFD、节的名称和要设置的标志。
    • 特点:结合了 bfd_make_section_with_flagsbfd_make_section_anyway 的功能。
  5. bfd_make_section_old_way

    • 功能:使用旧方法创建一个新的节。
    • 参数:只需要提供BFD和节的名称。
    • 特点:如果同名节已存在,则不会创建新的节,而是返回现有的节。这是较老的API,可能在新版本的BFD中不再推荐使用。
  6. bfd_set_section_flags

    • 功能:设置现有节的标志。
    • 参数:提供节和要设置的标志。
    • 特点:不创建新节,只修改现有节的属性。
  7. bfd_set_section_size

    • 功能:设置现有节的大小。
    • 参数:提供节和新的大小。
    • 特点:不创建新节,只修改现有节的大小。
  8. bfd_set_section_contents

    • 功能:设置现有节的内容。
    • 参数:提供BFD、节、数据缓冲区、偏移量和数据大小。
    • 特点:不创建新节,只修改现有节的内容。

这些函数提供了灵活的方式来创建和管理BFD中的节。选择哪个函数取决于你的具体需求,比如是否需要检查同名节的存在、是否需要设置特定的节属性等。在实际应用中,应根据目标文件格式和操作需求选择合适的函数。

1

如何新增section

我们主要通过c语言标准库中提供的工具来编辑 elf header。

1
2
3
#include <elf.h>
#include <libelf.h>
#include <gelf.h>