我正在关注这篇文章,其中清楚地展示了如何将解释器添加到共享对象。
现在我正在尝试做同样的事情,但不添加行
const char interp_path[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
即我希望代码是
#include <stdio.h>
#include <stdlib.h>
void print_version()
{
printf("Library version 1.0\n");
exit(0);
}
编译为
gcc -shared -fPIC -Wl,--entry=print_version -o lib.so lib.c
并为我的
lib.so
添加口译员。
patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 lib.so
。这会导致以下错误:patchelf: cannot find section '.interp'. The input file is most likely statically linked
A
标志 [ 1] .interp PROGBITS 0000000000000318 000318 00001c 00 A 0 0 1
我试过了
echo -n /lib64/ld-linux-x86-64.so.2 > tempfile
objcopy --add-section .interp=tempfile --set-section-flags .interp=alloc,readonly lib.so libi.so
它抱怨告诉我
objcopy: libi.so: warning: allocated section `.interp' not in segment
。尽管使用 readelf
我得到了正确的标题部分,并且 patchelf
不再抱怨,但我无法按照我的意愿得到我的 libi.so
。
你能帮我吗?
正如我在评论中所说,
仅添加 .interp 部分是不够的,加载器将无法找到解释器,因为它不是可加载段的一部分,因此不会加载到内存中。除了该部分之外,您还必须添加/更新程序头并将其设为 PT_INTERP 类型,并根据解释器字符串的属性(现在位于 .interp 部分中)设置其值。
不幸的是,当我撰写评论时,我没有找到任何有用的工具来实现此目的...您可以使用像LIEF这样的库来添加自定义的
PT_INTERP
程序头(我不确定这个库是否支持它)不过)。
如果你想从头开始写if,概念上不会有困难。我假设您的代码是位置无关的(正如您所说,您的目标是共享对象)。您必须将新的
PT_INTERP
程序头附加到程序头表的末尾,然后相应地更新以下标头u200c(您可以使用 libbfd
提取标头信息):
将
sizeof(Elf64_Phdr)
或sizeof(Elf32_Phdr)
添加到e_entry
和e_shoff
,因为追加新的phdr后,入口点和节头表的基数将发生移动。还要将 1
添加到 e_phnum
(其中存储程序头的数量)。
由于节和节头的数据通常放在程序头表之后,因此移位会影响节位置的正确偏移;因此,您还必须将
sizeof(Elf64_Phdr)
或 sizeof(Elf32_Phdr)
添加到每个节标题的 sh_offset
字段。
要创建程序头本身,您可以使用
elf.h
(Elf64_Phdr
或 Elf32_Phdr
)中提供的程序头结构。这样初始化结构体:
p_type = 3 // Set p_type to PT_INTERP
p_flags = PF_R // Set permission for .interp to Readable
p_offset = (Your .interp offset)
p_vaddr = (Your .interp virtual address)
p_paddr = (Your .interp virtual address)
p_filesz = 28 // strlen(Interpreter string) + 1
p_memsz = 28
p_align = 1
然后您可以将初始化的结构写入文件中,然后将其注入程序头的末尾(正如我上面所说的)。