首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在运行程序时,如何验证使用了什么动态链接器?

在运行程序时,如何验证使用了什么动态链接器?
EN

Stack Overflow用户
提问于 2020-12-23 18:41:22
回答 2查看 682关注 0票数 2

我想验证运行程序时使用的动态链接器是否是通过filereadelf -lldd提到的。我的动机是拥有多个动态链接器,这些动态链接器存在于机器上的不同空间中,它们不应该混为一谈。

到目前为止,我发现验证动态链接器的最好方法是通过gdb。通过查看info proc mappings的输出,我可以确定哪个动态链接器被映射到地址空间并正在使用中。我试图避免使用gdb,因为它需要我通过它运行测试套件和其他东西。

使用LD_DEBUG环境变量似乎是另一种解决方案,它允许我在(或在)程序执行之后轻松地保存日志以进行验证。然而,我不知道哪一种选择会给我最好的信息。我认为scopeslibs可能是很好的选择,但是libs并没有总是提到动态链接器。例如,这是一个简单的hello world程序的输出:

代码语言:javascript
复制
$ 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选项看起来更有帮助,但我不明白输出是什么意思:

代码语言:javascript
复制
$ 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似乎是一个不错的选择,但我很难理解如何在这种情况下有效地使用它。

(谢谢你的帮助:)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-24 02:02:44

没有必要实际运行可执行文件来确定它将使用的ELF解释器。

我们可以使用静态工具,并保证我们可以得到完整的路径。

我们可以使用readelfldd的组合。

如果我们使用readelf -a,我们可以解析输出。

readelf输出的一部分:

代码语言:javascript
复制
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

代码语言:javascript
复制
000002e0: 2F6C6962 36342F7A 642D6C69 6E75782D  /lib64/zd-linux-
000002f0: 7838362D 36342E73 6F2E3200 00000000  x86-64.so.2.....

注意绳子看起来有点奇怪..。稍后再详细介绍..。

在“程序标题:”部分中,我们有:

代码语言:javascript
复制
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了,我们将得到它正在/将要使用的共享库的列表。我们会为fileGood这么做。通常情况下,这看起来像是被修改过的

代码语言:javascript
复制
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)
...

这是普通的可执行文件。这是ldd输出的fileBad

代码语言:javascript
复制
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是我创建的一个副本,我在其中修补了一个不存在的文件的解释器路径。

readelf数据中,我们知道解释器路径。我们可以检查那个文件的存在。如果它不存在,事情显然是坏的。

使用从readelf获得的解释器路径,我们可以为解释器找到来自ldd的输出行。

对于好的文件,ldd给了我们简单的解释器解析:

代码语言:javascript
复制
/lib64/ld-linux-x86-64.so.2 (0x00007f36d23ff000)

对于糟糕的文件,ldd给了我们:

代码语言:javascript
复制
/lib64/zd-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f3f4f821000)

因此,无论是ldd还是内核都检测到缺少的解释器并替换了默认的解释器。

如果我们试图从外壳中执行fileBad,就会得到:

代码语言:javascript
复制
fileBad: Command not found

如果我们试图从C程序中执行fileBad,就会得到一个ENOENT错误:

代码语言:javascript
复制
No such file or directory

由此我们知道,当我们执行exec* syscall时,内核没有尝试使用“默认”解释器。

因此,我们现在知道,我们确定ELF解释器路径的静态分析是有效的。

我们可以确信,我们提出的路径是到ELF解释器的路径,内核将映射到进程地址空间。

为了得到进一步的保证,如果需要,请下载内核源代码。查看文件:fs/binfmt_elf.c

我认为这已经足够了,但是在你的评论中回答这个问题

有了这个解决方案,我就不必在程序结束前竞相阅读/proc/<pid>/maps了吗?

没必要比赛。

我们可以控制fork过程。我们可以将子程序设置为在syscall ptrace下运行,这样我们就可以控制它的执行(注意,ptracegdbstrace使用的)。

在我们fork之后,但在我们exec之前,子进程可以请求exec的目标休眠,直到进程通过ptrace连接到它为止。

因此,在目标可执行文件执行一条指令之前,父程序可以检查/proc/pid/maps或其他任何东西。它可以通过ptrace控制执行,并最终分离以允许目标正常运行。

有什么方法可以预测接下来会生成什么PID,然后在/proc中等待它的创建吗?

给出你问题的第一部分的答案,这是一个没有意义的问题。

没有办法准确地预测我们fork过程的fork。如果我们能够确定系统下一步将使用的pid,就不能保证我们将赢得与在我们之前执行fork的另一个流程的竞争,并“得到”我们认为是我们的pid

票数 3
EN

Stack Overflow用户

发布于 2020-12-23 19:04:23

您可以为此使用LD_DEBUG=scopes

从我的机器输出的样本:

代码语言:javascript
复制
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,检查他们的这里和实验。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65429331

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档