在 ELF 文件中定位全局偏移 Table
Locating the Global Offset Table in an ELF file
如何在我正在解析的 ELF 文件中找到 .got 部分的偏移量?
我不想按名称搜索该部分,因为我不想依赖它。当我用其他东西更改部分名称时,二进制文件仍然有效。
简短回答:在一般情况下,您不能。
GOT 在链接视图中
进程的链接视图由 header table 部分给出。 GOT 可以通过以下方式定位:
$ readelf -S $elf
[...]
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[29] .got PROGBITS 00000000003a2d80 001a2d80
0000000000000278 0000000000000008 WA 0 0 8
[30] .got.plt PROGBITS 00000000003a3000 001a3000
0000000000000078 0000000000000008 WA 0 0 8
你只能通过节名来识别GOT(可以是任何东西)。 GOT 条目使用 SHT_PROGBITS(与 ELF 文件的许多其他位一样),因此您不能使用节类型来识别它们。
此外,header table 部分在运行时不需要,也不需要出现在 ELF 文件中。
执行视图
我们可以改用执行视图吗?
程序给出的执行视图headertable。但是,在程序headertable中,并没有真正意义上的(non-PLT)GOT。运行时不关心 GOT 条目位于何处。它们也可以分散在数据段中的任何地方*。重要的是(运行时)重定位 table.
中存在正确的重定位
使用动态部分
动态链接器需要被告知 PLT GOT 条目在哪里 (.got.plt
)。这是由动态部分的 DT_PLTGOT
条目给出的。但是,它只给出了 GOT PLT 开始的(运行时,虚拟内存)位置table:你不知道它的大小。
使用重定位table
您可以尝试检查搬迁 tables:
您应该能够通过查看 PLT 重定位来推断 PLT GOT 的大小;
您可以通过查看 non-PLT 重定位来推断 non-PLT GOT 的位置和大小。
如果我正在查看 libc 的非 PLT 重定位 table(在 x86_64 上),我会得到一堆 R_X86_64_GLOB_DAT
条目:
$ readelf -r $elf
[...]
0000003a2da0 052c00000006 R_X86_64_GLOB_DAT 00000000003a4708 stderr + 0
0000003a2da8 061400000006 R_X86_64_GLOB_DAT 00000000003a85d0 error_one_per_line + 0
0000003a2db0 06eb00000006 R_X86_64_GLOB_DAT 00000000003a57d0 __malloc_initialize_ho + 0
0000003a2db8 07f300000006 R_X86_64_GLOB_DAT 00000000003a4720 __morecore + 0
0000003a2dc8 02a400000006 R_X86_64_GLOB_DAT 00000000003a8998 __key_encryptsession_p + 0
0000003a2dd0 061000000006 R_X86_64_GLOB_DAT 00000000003a3ec8 __progname_full + 0
0000003a2dd8 049c00000006 R_X86_64_GLOB_DAT 00000000003a4010 __ctype32_tolower + 0
0000003a2de0 011900000006 R_X86_64_GLOB_DAT 00000000003a5fb8 _environ + 0
0000003a2de8 000300000006 R_X86_64_GLOB_DAT 0000000000000000 _rtld_global + 0
0000003a2df0 011000000006 R_X86_64_GLOB_DAT 00000000003a3ec0 __progname + 0
0000003a2df8 04ff00000006 R_X86_64_GLOB_DAT 00000000003a32c4 argp_err_exit_status + 0
0000003a2e08 04ce00000006 R_X86_64_GLOB_DAT 00000000003a8538 mallwatch + 0
0000003a2e10 00bc00000006 R_X86_64_GLOB_DAT 00000000003a87d8 __rcmd_errstr + 0
0000003a2e18 056400000006 R_X86_64_GLOB_DAT 00000000003a48e0 __vdso_clock_gettime + 0
[...]
我们几乎找到了non-PLT GOT的地址:
$ readelf -S $elf
[...]
[29] .got PROGBITS 00000000003a2d80 001a2d80
0000000000000278 0000000000000008 WA 0 0 8
缺少4个GOT条目(我不知道为什么...)。
备注
(*):这意味着甚至可能没有任何 (non-PLT) GOT。
如何在我正在解析的 ELF 文件中找到 .got 部分的偏移量?
我不想按名称搜索该部分,因为我不想依赖它。当我用其他东西更改部分名称时,二进制文件仍然有效。
简短回答:在一般情况下,您不能。
GOT 在链接视图中
进程的链接视图由 header table 部分给出。 GOT 可以通过以下方式定位:
$ readelf -S $elf [...] Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [29] .got PROGBITS 00000000003a2d80 001a2d80 0000000000000278 0000000000000008 WA 0 0 8 [30] .got.plt PROGBITS 00000000003a3000 001a3000 0000000000000078 0000000000000008 WA 0 0 8
你只能通过节名来识别GOT(可以是任何东西)。 GOT 条目使用 SHT_PROGBITS(与 ELF 文件的许多其他位一样),因此您不能使用节类型来识别它们。
此外,header table 部分在运行时不需要,也不需要出现在 ELF 文件中。
执行视图
我们可以改用执行视图吗?
程序给出的执行视图headertable。但是,在程序headertable中,并没有真正意义上的(non-PLT)GOT。运行时不关心 GOT 条目位于何处。它们也可以分散在数据段中的任何地方*。重要的是(运行时)重定位 table.
中存在正确的重定位使用动态部分
动态链接器需要被告知 PLT GOT 条目在哪里 (.got.plt
)。这是由动态部分的 DT_PLTGOT
条目给出的。但是,它只给出了 GOT PLT 开始的(运行时,虚拟内存)位置table:你不知道它的大小。
使用重定位table
您可以尝试检查搬迁 tables:
您应该能够通过查看 PLT 重定位来推断 PLT GOT 的大小;
您可以通过查看 non-PLT 重定位来推断 non-PLT GOT 的位置和大小。
如果我正在查看 libc 的非 PLT 重定位 table(在 x86_64 上),我会得到一堆 R_X86_64_GLOB_DAT
条目:
$ readelf -r $elf [...] 0000003a2da0 052c00000006 R_X86_64_GLOB_DAT 00000000003a4708 stderr + 0 0000003a2da8 061400000006 R_X86_64_GLOB_DAT 00000000003a85d0 error_one_per_line + 0 0000003a2db0 06eb00000006 R_X86_64_GLOB_DAT 00000000003a57d0 __malloc_initialize_ho + 0 0000003a2db8 07f300000006 R_X86_64_GLOB_DAT 00000000003a4720 __morecore + 0 0000003a2dc8 02a400000006 R_X86_64_GLOB_DAT 00000000003a8998 __key_encryptsession_p + 0 0000003a2dd0 061000000006 R_X86_64_GLOB_DAT 00000000003a3ec8 __progname_full + 0 0000003a2dd8 049c00000006 R_X86_64_GLOB_DAT 00000000003a4010 __ctype32_tolower + 0 0000003a2de0 011900000006 R_X86_64_GLOB_DAT 00000000003a5fb8 _environ + 0 0000003a2de8 000300000006 R_X86_64_GLOB_DAT 0000000000000000 _rtld_global + 0 0000003a2df0 011000000006 R_X86_64_GLOB_DAT 00000000003a3ec0 __progname + 0 0000003a2df8 04ff00000006 R_X86_64_GLOB_DAT 00000000003a32c4 argp_err_exit_status + 0 0000003a2e08 04ce00000006 R_X86_64_GLOB_DAT 00000000003a8538 mallwatch + 0 0000003a2e10 00bc00000006 R_X86_64_GLOB_DAT 00000000003a87d8 __rcmd_errstr + 0 0000003a2e18 056400000006 R_X86_64_GLOB_DAT 00000000003a48e0 __vdso_clock_gettime + 0 [...]
我们几乎找到了non-PLT GOT的地址:
$ readelf -S $elf [...] [29] .got PROGBITS 00000000003a2d80 001a2d80 0000000000000278 0000000000000008 WA 0 0 8
缺少4个GOT条目(我不知道为什么...)。
备注
(*):这意味着甚至可能没有任何 (non-PLT) GOT。