通过修改第三方so的elf符号表兼容redhat6.2下低版本glibc

2021-01-14 14:38:32 浏览数 (3)

背景:第三方so依赖glibc2.14版本,如何在不升级redhat 6.2自带的gblic2.12情况下,运行so?

结论:通过16进制编辑器修改so的elf符号表来解决这个问题,即强制让so里依赖高版本gblic的函数指向低版本的glibc。风险:有些函数在老版本下可能会出问题,具体需要多测试

首先评估so文件修改风险

通过ldd命令打印库文件所依赖的共享库列表,发现so依赖glibc2.14版本

代码语言:javascript复制
[root@bogon lee]# ldd libTaSESDK.so 
ldd: warning: you do not have execution permission for `./libTaSESDK.so'
./libTaSESDK.so: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./libTaSESDK.so)
        linux-vdso.so.1 =>  (0x00007fff530a5000)
        libstdc  .so.6 => /usr/lib64/libstdc  .so.6 (0x00007fe729ebf000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fe729c3b000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe729a24000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fe729683000)
        /lib64/ld-linux-x86-64.so.2 (0x0000003810600000)

而通过rpm命令发现当前系统对应的是glibc2.12版本

代码语言:javascript复制
[root@bogon lee]# rpm -qa | grep glibc
glibc-2.12-1.47.el6.x86_64
glibc-devel-2.12-1.47.el6.x86_64
glibc-common-2.12-1.47.el6.x86_64
glibc-headers-2.12-1.47.el6.x86_64

通过objdump -T 查看so 动态符号,发现so使用glibc 2.14 版本的 memcpy 函数

代码语言:javascript复制
[root@bogon lee]# objdump -T libTaSESDK.so | grep GLIBC_2.1.*
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.14  memcpy

通过objdump -T /lib64/libc.so.6发现当前系统 glibc 库提供的 memcpy 实现是 2.2.5 版本

代码语言:javascript复制
[root@bogon lib64]#  objdump -T /lib64/libc.so.6 | grep memcpy
0000003810e8fa80  w   DF .text  0000000000000009  GLIBC_2.2.5 wmemcpy
0000003810efdc70 g    DF .text  000000000000001b  GLIBC_2.4   __wmemcpy_chk
0000003810e87dd0 g   iD  .text  000000000000003c  GLIBC_2.2.5 memcpy
0000003810efbd50 g   iD  .text  000000000000003c  GLIBC_2.3.4 __memcpy_chk

接下来,要做的就是让so指向2.2.5版本

修改elf

readelf -sV libTaSESDK.so >1.txt

通过readelf 命令查看so的ELF 的符号表

定位到 .gnu.version_r ,其表示二进制程序实际依赖的库文件版本

上图发现,该so比较简单,目前就2处,可想而知,我们只要修改 0x0010: Name: GLIBC_2.14 Flags: none Version: 3这行。

如果行数比较多,可以通过Version版本与 .gnu.version的信息对应,再与.dynsym对应来找到对应的函数。关系是 .gnu.version通过偏移量 .dynsym里面函数对应的num*2找到对应的版本,然后.gnu.version通过version关联.gnu.version_r。具体可以自行百度,本次so比较简单,所以没有查这个。

.gnu.version_r 表是按照不同的库文件进行分段显示的,每个条目占用 0x10 也就是 16 个字节,上图偏移量为0x0054d0,加上 0x0010=0x0054E0找到如下

现在只要参考 0x0020: Name: GLIBC_2.2.5 Flags: none Version: 2 修改就行,把 0x0010: Name: GLIBC_2.14 Flags: none Version: 3(0x0054E0开始)改为 0x0020: Name: GLIBC_2.2.5 Flags: none Version: 2(0x0054d0 0x0020=0x0054F0)类似就行,相当与把gblic2.14降为2.2.5

.gnu.version_r 表中每个条目是 16 个字节的 Elfxx_Vernaux 结构体,其声明如下(Elfxx_Half 占用 2 个字节,Elfxx_Word 占用 4 个字节):

代码语言:javascript复制
typedef struct {
    Elfxx_Word    vna_hash;
    Elfxx_Half    vna_flags;
    Elfxx_Half    vna_other;
    Elfxx_Word    vna_name;
    Elfxx_Word    vna_next;
} Elfxx_Vernaux;

vna_hash 为 4 个字节的库名称的 hash 值,vna_other 为对应的 .gnu.version 表中符号的版本值,vna_name 指向库名称字符串的偏移量(也可以在 ELF 头中找到),vna_next 为下一个条目的位置(一般固定为 0x00000010)。

终于修改为如下

只有vna_other和vna_next有差异

0 人点赞