如何反转 R_X86_64_JUMP_SLOT 重定位?

How to reverse R_X86_64_JUMP_SLOT relocations?

我正在构建一个 ELF 二进制文件,它需要能够在 运行 时间处理和反转它自己的重定位。 (显然,反转将发生在单独的缓冲区中,而不是原始代码页中。)这样做的目的是内存中的模块内容可以进行 HMAC 运算,并与从磁盘上的模块计算出的已知良好值进行比较,以确保没有发生腐败。我知道这有点不寻常,但这是我们必须遵守的标准要求。

我已经能够反转二进制 中的所有重定位,除了 在全局偏移 Table 中发生的 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 
...

这些偏移量指向全局偏移量 table(特别是 .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  [=12=]x0
    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 值来完成的。

然而,当我 运行 我的测试程序并查看动态链接后内存中的实际值时,这不是我所期望的。例如,运行现在,processRelocations0x5020 的 GOT 条目以值 0x7FF06A0F5AC5 结束。 运行 我的模块在内存中的基地址是 0x7ff06a0f4000。减去这个会得到 0x1AC5,它实际上看起来是 符号 table 条目 的值,用于该函数:

Symbol table '.dynsym' contains 34 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
   ...
   32: 0000000000001ac5   491 FUNC    GLOBAL DEFAULT   10 processRelocations

然而,符号table条目并不是我想要的;我需要恢复 0x1020 的原始值,但我也没有在符号 table 中的任何地方看到该值。

如何在 运行 时反转这些重定位以找到它们的原始值?

您可以通过使用 -fno-plt 进行编译来回避这个问题,这样您根本就没有任何 PLT 条目,相关的惰性绑定机制也不会发挥作用。

call *printf@GOTPCREL(%rip) which forces early binding: resolving the GOT entries on process startup. This makes each call more efficient,一些发行版(例如 Arch GNU/Linux)已经在用这种方式编译它们的包了。

TL:DR:这通常是一个不错的选择,只是在当前的 GCC 和 clang 发行版配置中默认情况下(尚未)启用。