当程序为 运行 时,如何验证使用的是哪个动态链接器?
How can I verify what dynamic linker is used when a program is run?
我想验证当一个程序是 运行 时使用的动态链接器是通过 file
、readelf -l
或 ldd
提到的那个。我的动机源于有多个动态链接器,它们存在于机器上单独的 spaces 中,它们永远不应该混合匹配。
到目前为止,我发现验证动态链接器的最佳方法是通过 gdb
。通过查看 info proc mappings
的输出,我可以确定哪个动态链接器被映射到地址 space 并且正在使用中。我试图避免使用 gdb
,因为它需要我通过它 运行 测试套件和其他东西。
使用 LD_DEBUG
环境变量似乎是一种替代解决方案,可以让我在程序执行后(或期间)轻松保存日志以供验证。但是,我不确定哪个选项会给我最好的信息。我认为 scopes
或 libs
可能是不错的选择,但 libs
并不总是提到动态链接器。例如,这是一个简单的 hello world 程序的输出:
$ LD_DEBUG=libs ./test0
24579: find library=libc.so.6 [0]; searching
24579: search cache=/etc/ld.so.cache
24579: trying file=/lib/x86_64-linux-gnu/libc.so.6
24579:
24579:
24579: calling init: /lib/x86_64-linux-gnu/libc.so.6
24579:
24579:
24579: initialize program: ./test0
24579:
24579:
24579: transferring control: ./test0
24579:
hello world
24579:
24579: calling fini: ./test0 [0]
24579:
$ LD_DEBUG=libs ./test0-gnu-cross
24581: find library=libc.so.6 [0]; searching
24581: search path=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v4:/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v3:/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v2:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell:/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib (RPATH from file ./test0-gnu-cross)
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v4/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v3/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v2/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6
24581:
24581:
24581: calling init: /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24581:
24581:
24581: calling init: /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6
24581:
24581:
24581: initialize program: ./test0-gnu-cross
24581:
24581:
24581: transferring control: ./test0-gnu-cross
24581:
hello world
24581:
24581: calling fini: ./test0-gnu-cross [0]
24581:
如您所见,使用标准 Debian/GNU 工具链构建并使用系统动态链接器的程序 test0
并未说明这一点。
scopes
选项看起来更有用,但我不明白输出在说什么:
$ LD_DEBUG=scopes ./test0
24577:
24577: Initial object scopes
24577: object=./test0 [0]
24577: scope 0: ./test0 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
24577:
24577: object=linux-vdso.so.1 [0]
24577: scope 0: ./test0 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
24577: scope 1: linux-vdso.so.1
24577:
24577: object=/lib/x86_64-linux-gnu/libc.so.6 [0]
24577: scope 0: ./test0 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
24577:
24577: object=/lib64/ld-linux-x86-64.so.2 [0]
24577: no scope
24577:
hello world
$ LD_DEBUG=scopes ./test0-gnu-cross
24576:
24576: Initial object scopes
24576: object=./test0-gnu-cross [0]
24576: scope 0: ./test0-gnu-cross /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24576:
24576: object=linux-vdso.so.1 [0]
24576: scope 0: ./test0-gnu-cross /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24576: scope 1: linux-vdso.so.1
24576:
24576: object=/usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 [0]
24576: scope 0: ./test0-gnu-cross /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24576:
24576: object=/usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2 [0]
24576: no scope
24576:
hello world
综上所述,我想找到一种验证正在使用的动态链接器的好方法。除非你能想到更好的选择,否则 LD_DEBUG
似乎是个不错的选择,但我很难理解在这种情况下如何有效地使用它。
感谢您的帮助:)
你可以用 LD_DEBUG=scopes
来做这个
我机器的示例输出:
LD_DEBUG=scopes ./hello
17513:
17513: Initial object scopes
17513: object=./hello [0]
17513: scope 0: ./hello /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
17513:
17513: object=linux-vdso.so.1 [0]
17513: scope 0: ./hello /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
17513: scope 1: linux-vdso.so.1
17513:
17513: object=/lib/x86_64-linux-gnu/libc.so.6 [0]
17513: scope 0: ./hello /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
17513:
17513: object=/lib64/ld-linux-x86-64.so.2 [0]
17513: no scope
17513:
Hello world
寻找没有作用域的对象。
此外,LD_DEBUG 只有几个值,请检查它们 here 并进行实验。
不需要实际上运行可执行文件来确定它的ELF解释器将使用。
我们可以使用静态工具,保证获取到完整路径
我们可以使用 readelf
和 ldd
的组合。
如果我们使用readelf -a
,我们可以解析输出。
readelf
输出的一部分:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000000002e0 000002e0
000000000000001c 0000000000000000 A 0 0 1
注意 .interp
部分的 地址 。是0x2e0
.
如果我们打开可执行文件并查找该偏移量,我们可以读取 ELF 解释器字符串。例如,这里是 [我将称之为] fileBad
:
000002e0: 2F6C6962 36342F7A 642D6C69 6E75782D /lib64/zd-linux-
000002f0: 7838362D 36342E73 6F2E3200 00000000 x86-64.so.2.....
请注意,该字符串似乎有点奇怪……稍后会详细介绍……
在“程序头:”部分下,我们有:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002a0 0x00000000000002a0 R 0x8
INTERP 0x00000000000002e0 0x00000000000002e0 0x00000000000002e0
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/zd-linux-x86-64.so.2]
再次注意 0x2e0
文件偏移量。这可能是获取 ELF 解释器路径的更简单方法。
现在我们有了 ELF 解释器的完整路径。
我们现在可以执行 ldd /path/to/executable
,我们将获得它 is/will 使用的共享库的列表。我们将为 fileGood
执行此操作。通常,这看起来像 [redacted]:
linux-vdso.so.1 (0x00007ffc96d43000)
libpython3.7m.so.1.0 => /lib64/libpython3.7m.so.1.0 (0x00007f36d1ee2000)
...
libc.so.6 => /lib64/libc.so.6 (0x00007f36d1ac7000)
/lib64/ld-linux-x86-64.so.2 (0x00007f36d23ff000)
...
这是一个普通的可执行文件。这是 fileBad
的 ldd
输出:
linux-vdso.so.1 (0x00007ffc96d43000)
libpython3.7m.so.1.0 => /lib64/libpython3.7m.so.1.0 (0x00007f36d1ee2000)
...
libc.so.6 => /lib64/libc.so.6 (0x00007f36d1ac7000)
/lib64/zd-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f3f4f821000)
...
好的,解释一下...
fileGood
是标准可执行文件 [/bin/vi
在我的系统上]。然而,fileBad
是一个 copy,我在 patched non-existent 文件的解释器路径
从 readelf
数据中,我们知道解释器路径。我们可以检查该文件是否存在。如果它不存在,那么事情[显然]很糟糕。
利用我们从readelf
得到的解释器路径,我们可以从ldd
找到解释器的输出行。
对于好的文件,ldd
给了我们简单的解释器解析:
/lib64/ld-linux-x86-64.so.2 (0x00007f36d23ff000)
对于坏文件,ldd
给了我们:
/lib64/zd-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f3f4f821000)
因此,ldd
或内核检测到丢失的解释器并替换为默认解释器。
如果我们尝试从 shell 执行 fileBad
,我们会得到:
fileBad: Command not found
如果我们尝试从 C 程序执行 fileBad
,我们会收到 ENOENT
错误:
No such file or directory
由此我们知道 内核 没有尝试使用“默认" 当我们执行 exec*
系统调用时的解释器。
因此,我们现在知道我们为确定 ELF 解释器路径所做的静态分析是有效的。
我们可以确信我们想出的路径是[将是 ] 内核 将映射到进程地址 space.
的 ELF 解释器的路径
为了进一步保证,如果需要,请下载内核源代码。查看文件:fs/binfmt_elf.c
我认为这已经足够了,但要回答您置顶评论中的问题
with that solution would I not have to race to read /proc/<pid>/maps
before the program terminates?
没必要比赛。
我们可以控制fork
进程。我们可以在[系统调用]ptrace
下设置child到运行,这样我们就可以控制它的执行(注意ptrace
就是gdb
和strace
使用).
在我们 fork
之后,但在我们 exec
之前,child 可以请求 exec
的目标休眠,直到一个进程通过 ptrace
.
因此,parent 可以在目标可执行文件执行单个指令之前检查 /proc/pid/maps
[或其他]。它可以通过 ptrace
控制执行 [并最终分离以允许目标正常 运行]。
Is there a way to predict what PID will be generated next and then wait on its creation in /proc
?
鉴于您问题第一部分的答案,这有点没有实际意义。
没有 方法可以[准确] 预测我们fork
过程的pid
。如果我们可以确定系统将使用next的pid
,则有no保证我们将赢得 race 对抗另一个进程 fork
[在我们之前] 并“获得”我们“认为”的 pid
将是我们的。
我想验证当一个程序是 运行 时使用的动态链接器是通过 file
、readelf -l
或 ldd
提到的那个。我的动机源于有多个动态链接器,它们存在于机器上单独的 spaces 中,它们永远不应该混合匹配。
到目前为止,我发现验证动态链接器的最佳方法是通过 gdb
。通过查看 info proc mappings
的输出,我可以确定哪个动态链接器被映射到地址 space 并且正在使用中。我试图避免使用 gdb
,因为它需要我通过它 运行 测试套件和其他东西。
使用 LD_DEBUG
环境变量似乎是一种替代解决方案,可以让我在程序执行后(或期间)轻松保存日志以供验证。但是,我不确定哪个选项会给我最好的信息。我认为 scopes
或 libs
可能是不错的选择,但 libs
并不总是提到动态链接器。例如,这是一个简单的 hello world 程序的输出:
$ LD_DEBUG=libs ./test0
24579: find library=libc.so.6 [0]; searching
24579: search cache=/etc/ld.so.cache
24579: trying file=/lib/x86_64-linux-gnu/libc.so.6
24579:
24579:
24579: calling init: /lib/x86_64-linux-gnu/libc.so.6
24579:
24579:
24579: initialize program: ./test0
24579:
24579:
24579: transferring control: ./test0
24579:
hello world
24579:
24579: calling fini: ./test0 [0]
24579:
$ LD_DEBUG=libs ./test0-gnu-cross
24581: find library=libc.so.6 [0]; searching
24581: search path=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v4:/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v3:/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v2:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell:/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1:/usr/local/gnu-cross/x86_64-linux-gnu/lib/x86_64:/usr/local/gnu-cross/x86_64-linux-gnu/lib (RPATH from file ./test0-gnu-cross)
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v4/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v3/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/glibc-hwcaps/x86-64-v2/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/haswell/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/tls/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/haswell/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/avx512_1/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/x86_64/libc.so.6
24581: trying file=/usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6
24581:
24581:
24581: calling init: /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24581:
24581:
24581: calling init: /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6
24581:
24581:
24581: initialize program: ./test0-gnu-cross
24581:
24581:
24581: transferring control: ./test0-gnu-cross
24581:
hello world
24581:
24581: calling fini: ./test0-gnu-cross [0]
24581:
如您所见,使用标准 Debian/GNU 工具链构建并使用系统动态链接器的程序 test0
并未说明这一点。
scopes
选项看起来更有用,但我不明白输出在说什么:
$ LD_DEBUG=scopes ./test0
24577:
24577: Initial object scopes
24577: object=./test0 [0]
24577: scope 0: ./test0 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
24577:
24577: object=linux-vdso.so.1 [0]
24577: scope 0: ./test0 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
24577: scope 1: linux-vdso.so.1
24577:
24577: object=/lib/x86_64-linux-gnu/libc.so.6 [0]
24577: scope 0: ./test0 /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
24577:
24577: object=/lib64/ld-linux-x86-64.so.2 [0]
24577: no scope
24577:
hello world
$ LD_DEBUG=scopes ./test0-gnu-cross
24576:
24576: Initial object scopes
24576: object=./test0-gnu-cross [0]
24576: scope 0: ./test0-gnu-cross /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24576:
24576: object=linux-vdso.so.1 [0]
24576: scope 0: ./test0-gnu-cross /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24576: scope 1: linux-vdso.so.1
24576:
24576: object=/usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 [0]
24576: scope 0: ./test0-gnu-cross /usr/local/gnu-cross/x86_64-linux-gnu/lib/libc.so.6 /usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2
24576:
24576: object=/usr/local/gnu-cross/x86_64-linux-gnu/lib/ld-linux-x86-64.so.2 [0]
24576: no scope
24576:
hello world
综上所述,我想找到一种验证正在使用的动态链接器的好方法。除非你能想到更好的选择,否则 LD_DEBUG
似乎是个不错的选择,但我很难理解在这种情况下如何有效地使用它。
感谢您的帮助:)
你可以用 LD_DEBUG=scopes
来做这个
我机器的示例输出:
LD_DEBUG=scopes ./hello
17513:
17513: Initial object scopes
17513: object=./hello [0]
17513: scope 0: ./hello /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
17513:
17513: object=linux-vdso.so.1 [0]
17513: scope 0: ./hello /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
17513: scope 1: linux-vdso.so.1
17513:
17513: object=/lib/x86_64-linux-gnu/libc.so.6 [0]
17513: scope 0: ./hello /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2
17513:
17513: object=/lib64/ld-linux-x86-64.so.2 [0]
17513: no scope
17513:
Hello world
寻找没有作用域的对象。 此外,LD_DEBUG 只有几个值,请检查它们 here 并进行实验。
不需要实际上运行可执行文件来确定它的ELF解释器将使用。
我们可以使用静态工具,保证获取到完整路径
我们可以使用 readelf
和 ldd
的组合。
如果我们使用readelf -a
,我们可以解析输出。
readelf
输出的一部分:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000000002e0 000002e0
000000000000001c 0000000000000000 A 0 0 1
注意 .interp
部分的 地址 。是0x2e0
.
如果我们打开可执行文件并查找该偏移量,我们可以读取 ELF 解释器字符串。例如,这里是 [我将称之为] fileBad
:
000002e0: 2F6C6962 36342F7A 642D6C69 6E75782D /lib64/zd-linux-
000002f0: 7838362D 36342E73 6F2E3200 00000000 x86-64.so.2.....
请注意,该字符串似乎有点奇怪……稍后会详细介绍……
在“程序头:”部分下,我们有:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002a0 0x00000000000002a0 R 0x8
INTERP 0x00000000000002e0 0x00000000000002e0 0x00000000000002e0
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/zd-linux-x86-64.so.2]
再次注意 0x2e0
文件偏移量。这可能是获取 ELF 解释器路径的更简单方法。
现在我们有了 ELF 解释器的完整路径。
我们现在可以执行 ldd /path/to/executable
,我们将获得它 is/will 使用的共享库的列表。我们将为 fileGood
执行此操作。通常,这看起来像 [redacted]:
linux-vdso.so.1 (0x00007ffc96d43000)
libpython3.7m.so.1.0 => /lib64/libpython3.7m.so.1.0 (0x00007f36d1ee2000)
...
libc.so.6 => /lib64/libc.so.6 (0x00007f36d1ac7000)
/lib64/ld-linux-x86-64.so.2 (0x00007f36d23ff000)
...
这是一个普通的可执行文件。这是 fileBad
的 ldd
输出:
linux-vdso.so.1 (0x00007ffc96d43000)
libpython3.7m.so.1.0 => /lib64/libpython3.7m.so.1.0 (0x00007f36d1ee2000)
...
libc.so.6 => /lib64/libc.so.6 (0x00007f36d1ac7000)
/lib64/zd-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f3f4f821000)
...
好的,解释一下...
fileGood
是标准可执行文件 [/bin/vi
在我的系统上]。然而,fileBad
是一个 copy,我在 patched non-existent 文件的解释器路径
从 readelf
数据中,我们知道解释器路径。我们可以检查该文件是否存在。如果它不存在,那么事情[显然]很糟糕。
利用我们从readelf
得到的解释器路径,我们可以从ldd
找到解释器的输出行。
对于好的文件,ldd
给了我们简单的解释器解析:
/lib64/ld-linux-x86-64.so.2 (0x00007f36d23ff000)
对于坏文件,ldd
给了我们:
/lib64/zd-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f3f4f821000)
因此,ldd
或内核检测到丢失的解释器并替换为默认解释器。
如果我们尝试从 shell 执行 fileBad
,我们会得到:
fileBad: Command not found
如果我们尝试从 C 程序执行 fileBad
,我们会收到 ENOENT
错误:
No such file or directory
由此我们知道 内核 没有尝试使用“默认" 当我们执行 exec*
系统调用时的解释器。
因此,我们现在知道我们为确定 ELF 解释器路径所做的静态分析是有效的。
我们可以确信我们想出的路径是[将是 ] 内核 将映射到进程地址 space.
的 ELF 解释器的路径为了进一步保证,如果需要,请下载内核源代码。查看文件:fs/binfmt_elf.c
我认为这已经足够了,但要回答您置顶评论中的问题
with that solution would I not have to race to read
/proc/<pid>/maps
before the program terminates?
没必要比赛。
我们可以控制fork
进程。我们可以在[系统调用]ptrace
下设置child到运行,这样我们就可以控制它的执行(注意ptrace
就是gdb
和strace
使用).
在我们 fork
之后,但在我们 exec
之前,child 可以请求 exec
的目标休眠,直到一个进程通过 ptrace
.
因此,parent 可以在目标可执行文件执行单个指令之前检查 /proc/pid/maps
[或其他]。它可以通过 ptrace
控制执行 [并最终分离以允许目标正常 运行]。
Is there a way to predict what PID will be generated next and then wait on its creation in
/proc
?
鉴于您问题第一部分的答案,这有点没有实际意义。
没有 方法可以[准确] 预测我们fork
过程的pid
。如果我们可以确定系统将使用next的pid
,则有no保证我们将赢得 race 对抗另一个进程 fork
[在我们之前] 并“获得”我们“认为”的 pid
将是我们的。