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 ()