我正在构建一个ELF二进制文件,需要在运行时能够处理并反转自身的重定位。(显然,反转将发生在单独的缓冲区中,而不是在原始代码页中。)这样做的目的是为了让内存中的模块内容进行HMAC计算,并与从磁盘上的模块计算出的已知好值进行比较,以确保没有发生损坏。我知道这有些不寻常,但这是我们必须遵守的标准要求。
我已经能够反转二进制文件中的所有重定位,除了 全局偏移表中发生的 R_X86_64_JUMP_SLOT
重定位。查看测试模块的.rela.plt
部分中的重定位条目,使用readelf -a mylib.so
命令,我看到了以下重定位:
Relocation section '.rela.plt' at offset 0x968 contains 20 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000005018 000100000007 R_X86_64_JUMP_SLO 0000000000000000 printf + 0
000000005020 002000000007 R_X86_64_JUMP_SLO 0000000000001ac5 processRelocations + 0
000000005028 000300000007 R_X86_64_JUMP_SLO 0000000000000000 memcpy + 0
000000005030 000400000007 R_X86_64_JUMP_SLO 0000000000000000 puts + 0
...
这些偏移量指向全局偏移表(特别是
.got.plt
),因此我可以使用 objdump -d -s -j .plt -j .got.plt mylib.so
获取更多信息。Disassembly of section .got.plt:
0000000000005000 <_GLOBAL_OFFSET_TABLE_>:
5000: 80 4e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .N..............
...
5018: 10 10 00 00 00 00 00 00 20 10 00 00 00 00 00 00 ........ .......
5028: 30 10 00 00 00 00 00 00 40 10 00 00 00 00 00 00 0.......@.......
...
Disassembly of section .plt:
0000000000001000 <.plt>:
1000: ff 35 02 40 00 00 pushq 0x4002(%rip) # 5008 <_GLOBAL_OFFSET_TABLE_+0x8>
1006: f2 ff 25 03 40 00 00 bnd jmpq *0x4003(%rip) # 5010 <_GLOBAL_OFFSET_TABLE_+0x10>
100d: 0f 1f 00 nopl (%rax)
1010: f3 0f 1e fa endbr64
1014: 68 00 00 00 00 pushq $0x0
1019: f2 e9 e1 ff ff ff bnd jmpq 1000 <.plt>
101f: 90 nop
1020: f3 0f 1e fa endbr64
请注意,此二进制文件为小端。因此,
.got.plt
中的偏移量,即0x1010、0x1020、0x1030...
似乎总是指向相应PLT条目中的endbr64
指令。这类似于这个答案中的信息,该答案引用“在启动时,动态链接器使用指向适当PLT条目的第二条指令的地址填充GOT插槽。”但是,在我的情况下,它似乎指向PLT条目中的第四条指令。该答案进一步建议通过将模块的基地址添加到原始GOT值来执行此重定位。然而,当我运行我的测试程序并查看动态链接后内存中的实际值时,它与我预期的不同。例如,现在运行,
processRelocations
的GOT条目位于0x5020
处,最终值为0x7FF06A0F5AC5
。该运行时模块的基地址在内存中为0x7ff06a0f4000
。减去这个值得到的结果是0x1AC5
,实际上是该函数的符号表条目的值。Symbol table '.dynsym' contains 34 entries:
Num: Value Size Type Bind Vis Ndx Name
...
32: 0000000000001ac5 491 FUNC GLOBAL DEFAULT 10 processRelocations
然而,符号表项并不是我想要的;我需要回到原始值 0x1020
,但我在符号表中也没有看到这个值。
我如何在运行时反转这些重定位以找到它们的原始值?
-fno-plt
进行编译吗?这样就可以完全不需要PLT条目,也希望不需要它们关联的惰性绑定机制。(GCC将使用call *printf@GOTPCREL(%rip)
,这会强制进行早期绑定:在进程启动时解析GOT条目) - Peter Cordes-fno-plt
至少可以解决我的问题。谢谢!如果你把这个作为答案,如果没有更好的解决方案,我会接受它。 - Mitch Lindgren