用特定动态链接器和LIBC执行ELF

Q: 在某x64环境中有个32位ELF,假设叫some。现将some及其依赖库(包含动态链接器)复制到另一个环境中,第一想让some跑起来,第二想用gdb调试some及其依赖库。

在原环境中用”ldd some”确认some及其依赖库(包含动态链接器)如下:

some
ld-2.12.2.so
libc-2.12.2.so
libcrypto.so.1.0.2
libdl-2.12.2.so

A: scz 2021-04-09 14:27

这个问题比较复杂,但确实有解。为了增加演示难度,将some迁移到一个不同发行版x64环境中。若原环境与新环境的内核、GLIBC相差巨大,估计要歇菜,不考虑这种情形。后续演示操作均在新环境中进行。

简单展示新环境:

$ uname -a
Linux ... 3.10.0-862.14.4.el7.x86_64 #1 SMP ... x86_64 GNU/Linux

$ ldd $(which id)
        linux-vdso.so.1 =>  (0x00007ffff7ffa000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007ffff7bb4000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ffff77e7000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007ffff7585000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007ffff7381000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ffff7ddb000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ffff7165000)

检查some及其依赖库:

$ chmod +x *
$ file -b some
ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.4.9, not stripped

尽管一个是2.4.9,另一个是3.10.0,这种不算相差巨大。

$ ldd some
        not a dynamic executable

ldd无法用于some,换种方式:

$ LD_TRACE_LOADED_OBJECTS=1 LD_WARN=yes LD_BIND_NOW=yes ./some
-bash: ./some: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

提示很明确,some所用动态链接器不存在。参看:

《动态链接器符号链接被破坏后的灾难恢复》
http://scz.617.cn:8/unix/201809191202.txt

《查看/修改ELF的动态链接器》
http://scz.617.cn:8/unix/201907041603.txt

可以用patchelf查看、修改some所用动态链接器:

$ patchelf --print-interpreter some
/lib/ld-linux.so.2

$ cp some some-new

$ patchelf --set-interpreter "./ld-2.12.2.so" some-new

$ patchelf --print-interpreter some-new
./ld-2.12.2.so

为什么改成ld-2.12.2.so?因为在原环境中some最终所用动态链接器就是它。

$ LD_TRACE_LOADED_OBJECTS=1 LD_WARN=yes LD_BIND_NOW=yes ./some-new
        linux-gate.so.1 =>  (0xf7fdf000)
        libcrypto.so.1.0.2 => not found
        libc.so.6 => not found
Segmentation fault

已经有进展,可以看到依赖库,该LD_LIBRARY_PATH上场了。

$ LD_LIBRARY_PATH=. LD_TRACE_LOADED_OBJECTS=1 LD_WARN=yes LD_BIND_NOW=yes ./some-new
        linux-gate.so.1 =>  (0xf7fdf000)
        libcrypto.so.1.0.2 => ./libcrypto.so.1.0.2 (0xf7dc8000)
        libc.so.6 => not found
        libdl.so.2 => not found
        libc.so.6 => not found
Segmentation fault

只有libcrypto用了当前目录下的版本,libc、libdl仍然未找到,为什么?该LD_DEBUG上场了。

$ LD_DEBUG=libs LD_LIBRARY_PATH=. LD_TRACE_LOADED_OBJECTS=1 LD_WARN=yes LD_BIND_NOW=yes ./some-new
    123059:     find library=libcrypto.so.1.0.2 [0]; searching
    123059:      search path=./tls/i686/sse2:./tls/i686:./tls/sse2:./tls:./i686/sse2:./i686:./sse2:.            (LD_LIBRARY_PATH)
    123059:       trying file=./tls/i686/sse2/libcrypto.so.1.0.2
...
    123059:       trying file=./libcrypto.so.1.0.2
    123059:
    123059:     find library=libc.so.6 [0]; searching
    123059:      search path=./tls/i686/sse2:./tls/i686:./tls/sse2:./tls:./i686/sse2:./i686:./sse2:.            (LD_LIBRARY_PATH)
    123059:       trying file=./tls/i686/sse2/libc.so.6
...
    123059:       trying file=./libc.so.6
...
    123059:       trying file=./libdl.so.2
...
    123059:       trying file=/usr/lib/libc.so.6
    123059:
        linux-gate.so.1 =>  (0xf7fdf000)
        libcrypto.so.1.0.2 => ./libcrypto.so.1.0.2 (0xf7dc8000)
        libc.so.6 => not found
        libdl.so.2 => not found
        libc.so.6 => not found
Segmentation fault

some-new在找

./libc.so.6
./libdl.so.2

当前目录下只有

libc-2.12.2.so
libdl-2.12.2.so

所以找不到。用符号链接解决该问题:

$ ln -s libc-2.12.2.so libc.so.6
$ ln -s libdl-2.12.2.so libdl.so.2

再次奇技淫巧ldd:

$ LD_LIBRARY_PATH=. LD_TRACE_LOADED_OBJECTS=1 LD_WARN=yes LD_BIND_NOW=yes ./some-new
        linux-gate.so.1 =>  (0xf7fdf000)
        libcrypto.so.1.0.2 => ./libcrypto.so.1.0.2 (0xf7dc8000)
        libc.so.6 => ./libc.so.6 (0xf7c66000)
        libdl.so.2 => ./libdl.so.2 (0xf7c62000)
        ./ld-2.12.2.so (0xf7fe0000)

然后测试gdb

$ gdb -q -nx ./some-new

(gdb) info files
...
        Entry point: 0x804ea38
...

(gdb) x/15i 0x804ea38
   0x804ea38 <_start>:  xor    %ebp,%ebp
   0x804ea3a <_start+2>:        pop    %esi
   0x804ea3b <_start+3>:        mov    %esp,%ecx
   0x804ea3d <_start+5>:        and    $0xfffffff0,%esp
   0x804ea40 <_start+8>:        push   %eax
   0x804ea41 <_start+9>:        push   %esp
   0x804ea42 <_start+10>:       push   %edx
   0x804ea43 <_start+11>:       push   $0x80631c0
   0x804ea48 <_start+16>:       push   $0x8063160
   0x804ea4d <_start+21>:       push   %ecx
   0x804ea4e <_start+22>:       push   %esi
   0x804ea4f <_start+23>:       push   $0x804d200
   0x804ea54 <_start+28>:       call   0x804cef0 <__libc_start_main@plt>
   0x804ea59 <_start+33>:       hlt
   0x804ea5a <_start+34>:       nop

(gdb) b *0x804d200
Breakpoint 1 at 0x804d200

在main()上设断

set environment LD_LIBRARY_PATH=.
set startup-with-shell off

上面2步是必须的

display/5i $pc
set backtrace past-main on
set backtrace past-entry on
set pagination off
set disassembly-flavor intel

上面几步不是必须的

(gdb) r

Breakpoint 1, 0x0804d200 in main ()
1: x/5i $pc
=> 0x804d200 <main>:    push   ebp
   0x804d201 <main+1>:  mov    ebp,esp
   0x804d203 <main+3>:  push   edi
   0x804d204 <main+4>:  push   esi
   0x804d205 <main+5>:  push   ebx
(gdb) si
0x0804d201 in main ()
1: x/5i $pc
=> 0x804d201 <main+1>:  mov    ebp,esp
   0x804d203 <main+3>:  push   edi
   0x804d204 <main+4>:  push   esi
   0x804d205 <main+5>:  push   ebx
   0x804d206 <main+6>:  and    esp,0xfffffff0
(gdb) bt
#0  0x0804d201 in main ()
#1  0xf7c7eb67 in __libc_start_main () from ./libc.so.6
#2  0x0804ea59 in _start ()
(gdb) info proc mappings
...
        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8066000    0x1e000        0x0 /tmp/scz/some-new
...
        0xf7c62000 0xf7c64000     0x2000        0x0 /tmp/scz/libdl-2.12.2.so
...
        0xf7c66000 0xf7dc1000   0x15b000        0x0 /tmp/scz/libc-2.12.2.so
...
        0xf7dc8000 0xf7fc0000   0x1f8000        0x0 /tmp/scz/libcrypto.so.1.0.2
...
        0xf7fe0000 0xf7ffc000    0x1c000        0x0 /tmp/scz/ld-2.12.2.so
...

A: John Reiser 2004-06-29

可以不Patch出some-new,直接调试”动态链接器+some”。

$ gdb -q -nx ./ld-2.12.2.so

display/5i $pc
set backtrace past-main on
set backtrace past-entry on
set pagination off
set disassembly-flavor intel
b *_dl_init_internal
set startup-with-shell off
r --library-path . ./some

"--library-path ."相当于"set environment LD_LIBRARY_PATH=."

断在_dl_init_internal()时检查内存映射:

(gdb) info proc mappings
process 13445
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8066000    0x1e000        0x0 /tmp/scz/some
         0x8066000  0x8067000     0x1000    0x1e000 /tmp/scz/some
         0x8067000  0x8068000     0x1000    0x1f000 /tmp/scz/some
...

确认some的入口点(e_entry):

(gdb) shell objdump -f some | grep start
start address 0x0804ea38

确认main()所在:

(gdb) x/15i 0x0804ea38
   0x804ea38:   xor    ebp,ebp
   0x804ea3a:   pop    esi
   0x804ea3b:   mov    ecx,esp
   0x804ea3d:   and    esp,0xfffffff0
   0x804ea40:   push   eax
   0x804ea41:   push   esp
   0x804ea42:   push   edx
   0x804ea43:   push   0x80631c0
   0x804ea48:   push   0x8063160
   0x804ea4d:   push   ecx
   0x804ea4e:   push   esi
   0x804ea4f:   push   0x804d200
   0x804ea54:   call   0x804cef0
   0x804ea59:   hlt
   0x804ea5a:   nop
(gdb) b *0x804d200
Breakpoint 2 at 0x804d200
(gdb) c
Continuing.

Breakpoint 2, 0x0804d200 in ?? ()
1: x/5i $pc
=> 0x804d200:   push   ebp
   0x804d201:   mov    ebp,esp
   0x804d203:   push   edi
   0x804d204:   push   esi
   0x804d205:   push   ebx

已经在some中,但没有符号,可以手工加载符号。参看:

《2.49 GDB加载调试信息》

确定some的.text、.data基址:

(gdb) shell objdump -h some | grep -F .text
 11 .text         00015ffc  0804d200  0804d200  00005200  2**4
(gdb) shell objdump -h some | grep -F .data
 21 .data         000000ac  08067000  08067000  0001f000  2**5

手工加载符号:

(gdb) add-symbol-file some 0x804d200 -s .data 0x8067000
add symbol table from file "some" at
        .text_addr = 0x804d200
        .data_addr = 0x8067000
(gdb) x/5i $pc
=> 0x804d200 <main>:    push   ebp
   0x804d201 <main+1>:  mov    ebp,esp
   0x804d203 <main+3>:  push   edi
   0x804d204 <main+4>:  push   esi
   0x804d205 <main+5>:  push   ebx
(gdb) bt
#0  0x0804d200 in main ()
#1  0xf7c7eb67 in __libc_start_main () from ./libc.so.6
#2  0x0804ea59 in _start ()

Spread the word. Share this post!

Meet The Author

C/ASM程序员

Leave Comment