标题: 查看/修改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命令不会显示动态链接器,比如FreeBSD 6.1 的: $ 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 "输出中包含动态链接器,但FreeBSD的如下命令输出中并 不包含动态链接器: $ ldd $ LD_TRACE_LOADED_OBJECTS=1 在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