为什么`ldd` 和`(gdb) info sharedlibrary` 显示不同的库基地址?

逆向工程 linux 数据库 符号 图书馆
2021-06-13 06:44:27

我看到应该加载 libc 的三个不同的输出。

ldd 显示libc的加载地址在 0xf7e9e000

$ ldd ~/my_tool
    linux-gate.so.1 =>  (0xf7ffe000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e9e000)
    /lib/ld-linux.so.2 (0x56555000)

虽然 gdb 告诉我它是在 0xf7e96c60

(gdb) info sharedlibrary
From        To          Syms Read   Shared Object Library
0xf7fe1820  0xf7ff805f  Yes (*)     /lib/ld-linux.so.2
0xf7e96c60  0xf7f9643c  Yes (*)     /lib/i386-linux-gnu/libc.so.6

当我根据符号手动计算基地址时:

(gdb) print system
$1 = {<text variable, no debug info>} 0xf7ebb6b0 <system>

系统偏移

我得到这个地址:

0xf7ebb6b0-0x003b6b0 = 0xf7e80000

为什么呢?

calculated: 0xf7e80000
gdb:        0xf7e96c60
ldd:        0xf7e9e000
2个回答

ldd

由于ldd一些原因,该程序是错误的。

首先,ldd并不意味着确定加载地址是准确的。使用环境变量LD_TRACE_LOADED_OBJECTS

其次,ldd如 Guntram 所示,在启用 ASLR 的情况下永远不会正确。如果您有sudo访问权限,您可以非常简单地禁用它

$ LD_TRACE_LOADED_OBJECTS=1 /bin/bash | grep libc
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f44dae1b000)
$ LD_TRACE_LOADED_OBJECTS=1 /bin/bash | grep libc
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9b35341000)
$ LD_TRACE_LOADED_OBJECTS=1 /bin/bash | grep libc
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fef18efd000)
$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
0
$ LD_TRACE_LOADED_OBJECTS=1 /bin/bash | grep libc
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff75e7000)
$ LD_TRACE_LOADED_OBJECTS=1 /bin/bash | grep libc
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff75e7000)
$ LD_TRACE_LOADED_OBJECTS=1 /bin/bash | grep libc
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff75e7000)

您可以通过在后台启动 bash 实例并检查其映射文件来验证这一点。

$ bash &
[1] 30398
[1]  + 30398 suspended (tty input)  bash                                                                             
$ grep libc /proc/30398/maps | head -n1
7ffff75e7000-7ffff77a2000 r-xp 00000000 08:01 525269                     /lib/x86_64-linux-gnu/libc-2.19.so

info sharedlibrary

上报info sharedlibrary的地址是该.text区域的地址

请注意,在我拥有的特定 libc 中,.text 位于0x1f4a0.

$ readelf --wide --section-headers /lib/x86_64-linux-gnu/libc-2.19.so | grep text
  [12] .text             PROGBITS        000000000001f4a0 01f4a0 145c23 00  AX  0   0 16

在 GDB 中,如果我们查看 的加载地址libc,我们会看到它是在 加载的0x7ffff7a14000如果在系统上启用了 ASLR,这将在每次程序运行时发生变化。如果在 GDB 下运行它,它也会禁用 ASLR。set disable-randomization off在运行目标之前运行命令,您将观察到它每次运行都会发生变化。

gdb-peda$ info proc mapping
  ...
  0x7ffff7a14000     0x7ffff7bcf000   0x1bb000        0x0 /lib/x86_64-linux-gnu/libc-2.19.so
  0x7ffff7bcf000     0x7ffff7dcf000   0x200000   0x1bb000 /lib/x86_64-linux-gnu/libc-2.19.so
  0x7ffff7dcf000     0x7ffff7dd3000     0x4000   0x1bb000 /lib/x86_64-linux-gnu/libc-2.19.so
  0x7ffff7dd3000     0x7ffff7dd5000     0x2000   0x1bf000 /lib/x86_64-linux-gnu/libc-2.19.so
  ...

您可以通过检查它是否以 ELF 标头开头来验证这一点。

gdb-peda$ hexdump 0x00007ffff7a14000 4
0x0000 0x00007ffff7a14000 │ 7f 45 4c 46                                      │ .ELF
0x0004 0x00007ffff7a14004

如果我们加上该区域0x1f4a0偏移量.text,我们得到0x7ffff7a334a0

gdb-peda$ printf "%p\n",(0x00007ffff7a14000+0x1f4a0)
0x7ffff7a334a0

现在,如果我们查看info sharedlibrary,我们会看到这个地址。

gdb-peda$ info sharedlibrary libc
From                To                  Syms Read   Shared Object Library
0x00007ffff7a334a0  0x00007ffff7b790c3  Yes         /lib/x86_64-linux-gnu/libc.so.6

额外提示

在 GDB 中,您应该使用该info proc mapping命令来获取已加载模块的基址。

但是,这在 FreeBSD 上不可用,因为 GDB 不支持获取模块基地址所需的 FreeBSD 特定 API。相反,您必须使用该info proc mapping命令并自己执行转换(通过减去段的地址.text)。

首先,ASLR会在每次调用时在(稍微)不同的地址加载库,以帮助防止恶意软件。这就是为什么之间的地址lddgdb是不同的,所以他们可能会在每次运行时甚至是不同的gdb

如果我只是 grep 系统上的 libc 可执行段(64 位,因为我手头没有 32 位系统):

$ grep 'r-xp.*libc-2.19' /proc/*/maps|head

/proc/10987/maps:7fe455b72000-7fe455d2d000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/11880/maps:7f029dc1f000-7f029ddda000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/11884/maps:7f25a9c25000-7f25a9de0000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/15715/maps:7ffc713da000-7ffc71595000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/17705/maps:7fe9db80c000-7fe9db9c7000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/18558/maps:7fc248544000-7fc2486ff000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/20156/maps:7f0f8eb13000-7f0f8ecce000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/2139/maps:7fba4c097000-7fba4c252000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/2215/maps:7f934ed76000-7f934ef31000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
/proc/2224/maps:7f064d98f000-7f064db4a000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so

你会看到每个进程都以不同的方式映射同一个库。

此外,当加载程序加载一个库时,它会为它创建几个内存段。ELF 文件中的段被读入这些共享内存段,包括一些头文件这就是代码起始地址与段起始地址不同的原因。

例如,在我的系统上:

(gdb) info sharedLibrary
From                To                  Syms Read   Shared Object Library
0x00007ffff7a334a0  0x00007ffff7b790c3  Yes         /lib/x86_64-linux-gnu/libc.so.6

cat /proc/26271/maps 
7ffff7a14000-7ffff7bcf000 r-xp 00000000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
7ffff7bcf000-7ffff7dcf000 ---p 001bb000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
7ffff7dcf000-7ffff7dd3000 r--p 001bb000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so
7ffff7dd3000-7ffff7dd5000 rw-p 001bf000 08:16 23461990                   /lib/x86_64-linux-gnu/libc-2.19.so

所以你会看到代码段(可执行的,即设置了 x 位)的内存映射范围为 7ffff7a14000-7ffff7bcf000,但实际代码加载到的区域只是子集 7ffff7a334a0-7ffff7b790c3。

您可以在此处找到更详尽的解释