如何反转 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 值来完成的。
然而,当我 运行 我的测试程序并查看动态链接后内存中的实际值时,这不是我所期望的。例如,运行现在,processRelocations
的 0x5020
的 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 发行版配置中默认情况下(尚未)启用。
我正在构建一个 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
指令。这类似于
然而,当我 运行 我的测试程序并查看动态链接后内存中的实际值时,这不是我所期望的。例如,运行现在,processRelocations
的 0x5020
的 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 发行版配置中默认情况下(尚未)启用。