linux下的object文件加载器 | 宜武汇-ag真人国际厅网站

在windows下已经有了很多针对coff文件的加载器,如coffloader和cs的bof功能,但是linux上面相关功能还是欠缺的,因此本本文章介绍一下相关技术,并提供了实现代码

目标设定

期望可以加载上述文件所生成的test.o文件

elf文件中结构

我们可以用objdump -d test.o -m intel看一下这个load函数的汇编

我们可以发现,上述反编译代码的call指令的操作数全部是0,为了能正确找到call的正确位置,所以链接器会修改这个偏移值。怎么修改呢,需要根据rela节中的数据进行修改。rela节的表项结构体如下

我们可以使用readelf -r test.o获取函数的重定向节,一般来说.text的rela节的名字是.rela.text

relocation section '.rela.text' at offset 0x268 contains 5 entries: offset info type sym. value sym. name   addend 000000000018 000500000004 r_x86_64_plt32 0000000000000000 println - 4 000000000032 000300000002 r_x86_64_pc32 0000000000000000 .rodata - 4 000000000042 000400000004 r_x86_64_plt32 0000000000000000 test_func_call - 4 00000000004e 000700000004 r_x86_64_plt32 0000000000000000 debugln - 4 000000000058 000800000004 r_x86_64_plt32 0000000000000000 hello_world - 4 relocation section '.rela.eh_frame' at offset 0x2e0 contains 2 entries: offset info type sym. value sym. name   addend 000000000020 000200000002 r_x86_64_pc32 0000000000000000 .text   0 000000000040 000200000002 r_x86_64_pc32 0000000000000000 .text   23

我们可以看到其中的一行,这里面有6个值,但是结构体只有3个,这个原因我们下面会解释

000000000018 000500000004 r_x86_64_plt32 0000000000000000 println - 4
  • r_offset: 000000000018: 代表着这个重定向位置相对section首地址的偏移,这里就是用括号框起来的这个位置,
17: e8 [00 00 00 00] call 1c 
  • r_info: 000500000004:对于32位elf文件可进一步细分为 24 位符号表索引和 8 位类型字段,64位elf文件可以32位的的符号表索引和32位的类型字段
    • sym = r_info >> 32 = 5
    • type = r_info & 0xffffffff = 4
  • r_x86_64_plt32: 这个不是一个字段,type就是来决定r_x86_64_plt32
    • 这个类型可以在intel的ag真人国际厅网站官网中找到
    • 我们可以看到4代表的就是r_x86_64_plt32
  • 0000000000000000 println:这两个是从符号表中关联过来的,后面会详细介绍
  • r_addend: -4:这是一个很重要的字段,在重定位中有着很重要的作用,在后续的重定位指针章节中会详细介绍

我们可以通过readelf -s test.o读取符号节

  • st_name: 这里的起始是一个索引,对应strtab中的字符串。
    • 可以通过在strtab中读取这个值
  • st_shndx: 代表着该符号所在的节的序号,还有几个特殊的值
    • shn_undef表示这个符号并未在当前文件定义,这个文件没有这个符号的位置
    • shn_abs表示这个符号是一个绝对地址,不需要重定位
    • shn_common表示这个符号是用来定义对齐字节的。
  • st_value: 代表着该符号,相对于符号所在节起始地址的偏移
    • 若改符号st_shndx=shn_common,那么st_value代表着对齐字节数
  • 其他字段暂时用不到

重定位偏移

我们可以看到上面关于重定位类型的图,这里的s,a,l,p,g,got分别代表

  • a 代表用于计算可重定位字段值的被加数。
  • b 代表在执行期间加载到内存中的共享对象的基地址。一般来说,共享对象的基虚拟地址为0,但执行地址会有所不同。
  • g 代表重定位项符号在全局偏移表(got)中的偏移量,在执行期间该符号将位于此处。
  • got 代表全局偏移表的地址。
  • l 代表符号的过程链接表(plt)条目的位置(段偏移或地址)。
  • p 代表正在重定位的存储单元的位置(段偏移或地址)(使用 r_offset 计算)。
  • s 代表重定位项中索引所在符号的值。
  • z 代表重定位项中索引所在符号的大小。

到这里,就需要研究一下这些重定向模式了,但是值得一提的是,在这个加载器中很多类型都是相同的,比如说r_x86_64_plt32r_x86_64_pc32,因为我们根本没有plt表

为了重定位,我们需要将重定位的类型分成两种情况,

  • 程序内重定向:指的是一个.o程序当中某个函数对另一个函数的调用,对程序内字符串的引用等,比如说在这个例子中main函数中对test_func_call的调用
  • 程序外重定向:指的是程序调用外部函数,需要重定向程序外的函数的真实地址,比如说在这个例子中main中对hello_world,debugln的调用

程序内重定向的逻辑其实和标准的链接步骤相同,按照基本的规则进行链接即可。我们这里来看一下本例当中main函数对test_func_call的调用。

在重定位表中,这个对应如下表项

000000000042 000400000004 r_x86_64_plt32 0000000000000000 test_func_call - 4

这里我们可以看到我们修改的偏移是42字节,类型是r_x86_64_plt32,代表着修改的长度是32位,4字节,addend = -4。对应的汇编如下

根据手册这个类型的计算规则是l a - p,但是由于我们这里没有plt表,但是根据基本原理我们这里要这么计算

sym_real_address   r_addend - patch_real_address
  • sym_real_address: 符号的真实内存地址
  • patch_real_address: 在内存中修改的需要修改的起始地址
  • r_addend这是需要加上的,用来解决偏移问题的

同理,其他的模式也可以通过具体分析去写出来

程序外重定向就更加简单了,根本不需要考虑重定向类型。由于call指令的操作数记录的是相对rip的偏移,而真实的rip相对修改的地址有4/8个字节的偏差(根据操作数长度判定)。所以直接计算这个偏移即可

sym_real_address - patch_real_address - 4/8

原文链接:https://xz.aliyun.com/t/12615

网络摘文,本文作者:15h,如若转载,请注明出处:https://www.15cov.cn/2023/08/27/linux下的object文件加载器/

发表评论

邮箱地址不会被公开。 必填项已用*标注

网站地图