标题: 查看/修改ELF的动态链接器
创建: 2019-07-04 16:03 更新: 2019-07-05 09:27 链接: https://scz.617.cn/unix/201907041603.txt
不想看全文的,就一句话,用patchelf查看、修改ELF的动态链接器。
关于动态链接器,Linux用户可以看ld.so(8)。
动态链接版ELF从内核态获得执行时控制权首先交给ELF的动态链接器,而不是e_entry, 更不是main()。
除非在编译链接时给ld(1)指定了-static选项,否则Linux程序必需动态链接器。
有两种动态链接器,ld.so用于处理a.out格式(很古老的一种格式),ld-linux.so*用 于处理ELF格式。
Debian的ld-linux.so*是可以单独执行的,但FreeBSD 6.1的/libexec/ld-elf.so.1 就不行。可以单独执行的动态链接器在某些特定场景有意义,比如:
《动态链接器符号链接被破坏后的灾难恢复》 https://scz.617.cn/unix/201809191202.txt
本文对ELF的动态链接器本身不多解释,假设读者对ELF是了解的。说一个真实案例。
受组织之托逆向分析一个设备,千辛万苦之后确认这个设备是基于x86/FreeBSD 6.1 改的。
TK推荐过:
https://www.osboxes.org/freebsd/
这里有现成的FreeBSD 10.3/12.0的VMware映像。没有6.1,只好在VMware中自己装 x86/FreeBSD 6.1,这样可以自己编译某些静态版本的工具,比如xxd、lrzsz、gdb等 等。
有一天想弄个lsof到目标设备上。多说一句,如果只是想知道端口与进程的映射关系, 或许有替代方案,参看:
《x64/FreeBSD 10.3/12.0中lsof的替代方案》 https://scz.617.cn/unix/201906131429.txt
在FreeBSD 6.1中用ports自编译lsof,TK帮我找到了精确匹配版本:
http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/ports/distfiles/lsof_4.77D.freebsd.tar.bz2
这个链接居然没有被Google爬到,简直了。
在目标设备上执行时报错:
$ /tmp/lsof -lnPR -i4 ELF interpreter /libexec/ld-elf.so.1 not found Abort
从错误提示看出,lsof所用动态链接器是:
/libexec/ld-elf.so.1
在目标设备上找了一圏,只有:
$ ls -l /libexec/ld-elf32.so.1 lrw-r--r-- 1 root wheel 24 Mar 3 2015 /libexec/ld-elf32.so.1 -> /usr/libexec/ld-elf.so.1
受制于只读文件系统,无法通过创建符号链接解决:
$ ln -s /libexec/ld-elf32.so.1 /libexec/ld-elf.so.1 ln: /libexec/ld-elf.so.1: Read-only file system
至此,产生了两个需求:
a) 查看指定ELF的动态链接器 b) 修改指定ELF的动态链接器
FreeBSD 6.1的ELF工具不够强大,在Debian中检查自编译得到的lsof:
$ readelf -p .interp lsof
String dump of section '.interp': [ 0] /libexec/ld-elf.so.1
$ readelf -x .interp lsof $ readelf -x1 lsof
Hex dump of section '.interp': 0x080480d4 2f6c6962 65786563 2f6c642d 656c662e /libexec/ld-elf. 0x080480e4 736f2e31 00 so.1.
$ objdump -j .interp -hs lsof
lsof: file format elf32-i386
Sections: Idx Name Size VMA LMA File off Algn 0 .interp 00000015 080480d4 080480d4 000000d4 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA Contents of section .interp: 80480d4 2f6c6962 65786563 2f6c642d 656c662e /libexec/ld-elf. 80480e4 736f2e31 00 so.1.
$ readelf -Wl lsof ... Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align ... INTERP 0x0000d4 0x080480d4 0x080480d4 0x00015 0x00015 R 0x1 [Requesting program interpreter: /libexec/ld-elf.so.1] ...
$ patchelf --print-interpreter lsof /libexec/ld-elf.so.1
$ file -b lsof ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1, stripped
前面这些命令都可以查看指定ELF的动态链接器。Debian上"file -b
$ file -b lsof ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked (uses shared libs), stripped
FreeBSD 10.3/12.0的file命令会显示动态链接器,FreeBSD 6.1实在太老了。
此外,Linux的"ldd
$ ldd
在Debian上检查目标设备中的id命令,确认其动态链接器是:
/usr/libexec/ld-elf.so.1
我们需要将自编译得到的lsof的动态链接器改成上面这个。
有两种办法,一种是用patchelf直接修改ELF,另一种是在编译链接阶段指定动态链 接器。
$ cp lsof lsof-test $ patchelf --set-interpreter "/usr/libexec/ld-elf.so.1" lsof-test
或
$ cd /usr/ports/sysutils/lsof $ make CC=gcc
注意到最后一条命令是:
gcc -o lsof -fno-strict-aliasing -pipe -DHASEFFNLINK=i_effnlink -DHASF_VNODE \ -DHASCPUMASK_T -DHASSBSTATE -DHAS_KVM_VNODE -DHAS_UFS1_2 -DHAS_NO_SI_UDEV \ -DHAS_SI_PRIV -DHAS_SYS_SX_H -DFREEBSDV=6010 -DHASFDESCFS=2 -DHASPSEUDOFS \ -DHASNULLFS -DHAS9660FS -DHAS_NO_ISO_DEV -DHASIPv6 -DLSOF_VSTR=\"6.1-RELEASE\" \ -I/usr/src/sys -O2 dmnt.o dnode.o dnode1.o dproc.o dsock.o dstore.o arg.o \ main.o misc.o node.o print.o proc.o store.o usage.o -L./lib -llsof -lkvm
$ cd work/lsof_4.77D.freebsd/ $ gcc -Wl,-dynamic-linker,/usr/libexec/ld-elf.so.1 -o lsof-test \ -fno-strict-aliasing -pipe -DHASEFFNLINK=i_effnlink -DHASF_VNODE \ -DHASCPUMASK_T -DHASSBSTATE -DHAS_KVM_VNODE -DHAS_UFS1_2 -DHAS_NO_SI_UDEV \ -DHAS_SI_PRIV -DHAS_SYS_SX_H -DFREEBSDV=6010 -DHASFDESCFS=2 -DHASPSEUDOFS \ -DHASNULLFS -DHAS9660FS -DHAS_NO_ISO_DEV -DHASIPv6 -DLSOF_VSTR=\"6.1-RELEASE\" \ -I/usr/src/sys -O2 dmnt.o dnode.o dnode1.o dproc.o dsock.o dstore.o arg.o \ main.o misc.o node.o print.o proc.o store.o usage.o -L./lib -llsof -lkvm
两种办法得到的lsof-test在目标设备上均可执行。由于FreeBSD 6.1上存在:
/usr/libexec/ld-elf.so.1
于是lsof-test既可以在目标设备上运行,也可以在FreeBSD 6.1虚拟机上运行。
一般来说,尽可能向目标设备上传静态链接版本ELF,可以避免很多麻烦,但我没有找 到编译静态链接版本lsof的办法。
前面演示的patchelf还可以修改ELF的其他内容:
--set-interpreter INTERPRETER --print-interpreter
--print-soname --set-soname SONAME
--set-rpath RPATH --remove-rpath --shrink-rpath --allowed-rpath-prefixes PREFIXES --print-rpath --force-rpath
--add-needed LIBRARY --replace-needed LIB_ORIG LIB_NEW --remove-needed LIBRARY
--no-default-lib
Debian中可以这样安装patchelf:
$ aptitude install patchelf