ELF文件格式之.plt与.got表
x86架构的重定位操作需要修改代码段,arm架构重定位待修改的数据位于数据段(.got表),不需要修改代码段。
.plt节区与.got表#
外部函数引用重定位#
arm架构使用.plt节区存放外部函数调用的PLT条目,通过.plt节区索引到.got表中导入函数的实际地址(got表有点x86中的输入地址表的意思)。
例如_start函数调用libc.so的导出函数__libc_init()。
指令ADR R12, 0x504
将0x504的值传给R12,所以R12 = 0x504
指令ADD R12,R12,#0x3000
是将R12 = R12 + 0x3000 = 0x504 + 0x3000 = 0x3504
指令LDR PC,[R12,#(__libc_init_ptr -0x3504)]
将PC = [R12 + __libc_init_ptr - 0x3504] = [0x3504 + __libc_init_ptr - 0x3504] = [__libc_init_ptr], __libc_init_ptr是plt条目对应的.got表项的地址,所以最后将.got表中实际的函数地址传给PC并调用。
总结:#(__libc_init_ptr -0x3504 )就是__libc_init_ptr相对于0x3504的偏移,所以无论程序被加载到哪个基地址处,plt条目总是能得到__lib_init_ptr的实际地址。plt条目相当于做了一件事 :LDR PC,[函数对应.got表项地址],也就是获取函数的实际地址并跳转过去。
__libc_init_ptr是plt条目对应的.got表项的地址,.got表项中存放的是实际调用的外部函数的地址,需要在程序加载时由linker程序修复,(windows输入表修复)。
导出的全局数据重定位#
x86架构上全局数据重定位直接通过重定位表修改对应的指令机器码,arm架构索引全局数据的指令是通过相对偏移的,无需进行重定位(指令本身就是地址无关性),但是对于导出的全局数据,因为有其他模块引用,所以就需要通过修改.got表实现全局数据的重定位。
以访问导出全局数据__PREINIT_ARRAY__为例
指令LDR R1, =(__PREINIT_ARRAY___ptr - 0x568)
是将R1 = __PREINIT_ARRAY___ptr - 0x568
指令LDR R1,[PC,R1]
是将R1 = [PC + R1] = [PC + __PREINIT_ARRAY___ptr - 0x568 ] ,因为当前pc的值第三条指令的地址为0x568,所以R1 = [__PREINIT_ARRAY___ptr ]相当于获取了实际全局数据__PREINIT_ARRAY__的地址赋给R1.
__PREINIT_ARRAY___ptr指向的.got表项中存放的是全局数据实际的地址,会在程序加载的时候修复。
结论#
arm架构的外部函数引用重定位与导出的全局数据重定位都是通过.got表实现的,其中外部函数引用重定位还需要.plt条目参与。.got表中存放的是所有需要重定位修复的地址信息(32位就每4个字节一项,64位就每8个字节一项)。
注意:.plt条目也属于程序代码
操作系统如何进行重定位#
在程序加载时.got表中的数据要进行重定位,那么操作系统是如何索引到.got表并进行重定位呢?
.rel.dyn表和.rel.plt表#
通过readelf可以查看到这两个表,直接在ida中是查看不到的。(两个表相当于widnwos PE中的重定位表)
struct Elf32_Dyn
{
Elf32_Sword d_tag;
union{
Elf32_Word d_val;
Elf32_Addr d_ptr;
}d_un;
};
这两个节区可以通过.dynamic节区(等同于DYNAMIC segment)找到,.dynamic节区是一个Elf32_Dyn数组,数组每一项都会描述一个特定类型的节区,d_tag为其对应的类型。其中DT_REL类型为.rel.dyn节区,DT_JMPREL为.rel.plt节区。而d_ptr就指向其节区对应的偏移地址。
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
typedef struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
Elf64_Sxword r_addend; //加数
} Elf64_Rela;
.rel.dyn包含了除外部函数引用外需要重定位的数据的信息,.rel.plt包含了外部函数引用需要重定位的数据的信息。二者都是一个Elf32_Rel类型的数组。(一般情况下32位是Elf32_Rel类型。64位是Elf64_Rela类型,此类型包含一个显示加数)
.rel.dyn中的重定位类型为R_ARM_RELATIVE,R_ARM_GLOB_DAT和R_ARM_ABS32,.rel.plt重定位类型为R_ARM_JUMP_SLOT。
- R_ARM_RELATIVE类型是对导出的全局变量引用的重定位。
- R_ARM_GLOB_DAT和R_ARM_ABS32类型是对数据引用的重定位(r_info含有重定位类型信息和对应的数据符号表索引)
- R_ARM_JUMP_SLOT类型是对函数引用的重定位(r_info含有重定位类型信息和对应的函数调用的符号表索引)
r_offset:对于可重定位文件,该值表示节偏移。对于可执行文件或共享目标文件,该值表示受重定位影响的存储单元的虚拟地址。(最终指向.got表)
r_info:指定了需要重定位数据的符号表索引以及要应用的重定位类型。(如果需要重定位的是一个函数引用地址,那么就要包含函数名称信息)。计算方法如下。
#define ELF32_R_SYM(info) ((info)>>8)
#define ELF32_R_TYPE(info) ((unsigned char)(info))
#define ELF32_R_INFO(sym, type) (((sym)<<8)+(unsigned char)(type))
R_ARM_JUMP_SLOT重定位类型解析#
以.rel.plt中的需要重定位的__libc_init函数的Elf32_Rel结构为例进行解析。
r_offset为0x3FF0,指向对应的.got表项的虚拟地址。
r_info为0x316, ELF32_R_SYM(0x316) == 3,对应在.dynsym符号表中的索引为3。
typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
.dynsym符号表为一个Elf32_Sym类型的数组,st_name为对应在字符串表.dynstr中的偏移,此处为0x22。
R_ARM_RELATIVE重定位类型解析#
因为R_ARM_RELATIVE类型的重定位数据描述的是指向导出的全局变量的指针,当这个值也就是说其offset指向的.got表项是一个指针类型的变量,对应的表项值是一个导出的全局变量的地址。
以 Elf32_Rel <0x3FDC, 0x17> ; R_ARM_RELATIVE
类型为例进行解析,其ELF32_R_SYM(r_info) = 0x17 >> 8 = 0, 也就是其对应的符号表索引为0,因为其实对全局变量的指针数据进行重定位所以符号索引无意义。
offset指向.got表项中存放的是main函数的地址。在编译时此.got表项中存放的是main函数相对于默认0加载地址的静态地址,在liner加载此程序时会将此.got表项的值 + 实际加载基地址然后修复。(R_ARM_RELATIVEA类型带显式加数,修复的话是将显式加数 + 实际加载基地址然后修复)
linker程序通过.rel.dyn和.rel.plt中不同类型的重定位数据进行分类修复。对于R_ARM_RELATIVE类型的需要通过r_offset找到对应的.got表项然后将对应的表项值+加载基地址,对于R_ARM_JUMP_SLOT,R_ARM_GLOB_DAT和R_ARM_ABS32类型的信息需要通过r_info得到对应的符号表索引找到在符号表中的符号信息(具体使用哪个符号表需要从当前重定位section的sh_link字段中获取),然后计算出对应符号实际的地址后通过r_offset找到对应的.got表项并进行修复。(下面参考android 8.0中的linker源码)
总结#
linux在加载elf文件时就是通过.rel.dyn
和.rel.plt
找到对应的符号信息并修正对应.got表项中的数据。
注意:elf的输入表HOOK就可以通过在.dynsym
字符串表中查找需要hook的函数名称,然后计算出对应的符号表索引。通过符号表索引在.rel.dyn
和.rel.plt
表中寻找对应的项进而找到对应的.got
表项并更改地址为自己的地址。(也可以通过hash表快速定位)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】