在ctf中共享库的问题一直是一个非常非常非常恶心的问题,这里将会介绍动态链接库的命名以及如何修改一个程序依赖的动态链接库
0x7f387805e000 0x7f3878091000 r-xp 33000 0 /lib/x86_64-linux-gnu/libseccomp.so.2.4.3
主版本是重大升级,不会向上兼容(如python2和3)
次版本是增量升级,添加一些新的接口
发布版本是进行一些错误修正,性能改进等
用SO-NAME机制来记录共享库的依赖关系
每个库都有自己的SO-NAME知名库名字和主版本号如libc.so.2
SO-NAME相同的两个次版本号不同的库,次版本号大的兼容小的
在linux系统中,系统会为每个共享库在它所在的目录创建一个跟SO-NAME相同并且指向它的软连接如系统中存在共享库/lib/libfoo.so.2.6.1
那么linux共享库管理程序会为它创建一个指向它的软链接/lib/libfoo.so.2
也有一些不标准的命名,如libc.so.6 还有ld-2.6.1.so被命名为ld-linux.so
在运行时需要链接一个程序可以加 -lxxx
,如-lpthread
编译器会根据当前环境在系统中的相关路径查找最新版本的库一般用-L
指定搜索路径
对于链接方式分为动态和静态链接在ld使用-static
选项时会搜索静态链接库(如libc.a.2.23)默认是动态
如果DT_NEED存放绝对路径就从这个路径找
如果没有会在/lib,/usr/lib由 /etc/ld.so.conf配置文件指定的目录中查找共享库。
为了加快遍历速度,有一个叫ldconfig的程序各个共享库创建删除或更新相应的SO-NAME,把这些SO-NAME集中起来放到/etc/ld.so.cache
所以正常更新或替换共享库需要运行一次ldconfig
-share
选项表示输出结果是共享库类型
-Wl
选项可以传递给连接器选项 -Wl,-soname,my_soname
可以指定SO-NAME(如果不指定这个库就没有soname,用ldconfig也没用)
包含两步1.创建SO-NAME软连接。 2.告诉编译器和程序如何查找共享库
ldconfig -n shared_library_directory
在编译程序时提供-L
和-l
LD_LIBRARY_PATH也可以用来指定LD_LIBRARY_PATH临时改变某个应用程序的共享库查找路径而不会影响系统中其他程序
类似于/lib/ld-linux.so.2 -binary-path /home/user /bin/ls
。
LD_PRELOAD优先级更高,无论程序是否依赖动态库,被指定的动态库都会被加载
系统将会从下面搜索库
有一个项目可以下载很多版本的libc
https://github.com/matrix1001/glibc-all-in-one
➜ git clone https://github.com/matrix1001/glibc-all-in-one.git
➜ glibc-all-in-one ./update_list
➜ glibc-all-in-one cat list 可以看到获取到的库名字
➜ glibc-all-in-one ./download 2.23-0ubuntu10_i386 后面的库名字是上一条命令看到的任意一个
然后需要创建目录,拷贝文件到对应目录
➜ glibc-all-in-one sduo mkdir -p /glibc/2.27/64/lib/
➜ glibc-all-in-one sudo cp ./libs/2.27-3ubuntu1.2_amd64/* /glibc/2.27/64/lib
为了完成上面的操作写了一个简单的脚本,完成更新列表,下载库,拷贝的过程
import os
def download(LibcNameList):
for item in LibcNameList:
os.system("./download {}".format(item))
def mkDir(name):
for item in name:
os.system("sudo mkdir -p /glibc/{}/64/lib/".format(item))
os.system("sudo mkdir -p /glibc/{}/32/lib/".format(item))
def getName(LibcNameList):
name=[]
for item in LibcNameList:
if item.split("-")[0] in name or item.split("-")[0]=="" :
continue
else:
name.append(item.split("-")[0])
print("name:",name)
return name
os.system("./update_list")
f=open("./list","r")
content=f.read()
print(content)
LibcNameList=content.split("\n")
name=getName(LibcNameList)
mkDir(name)
download(LibcNameList)
for LibcName in LibcNameList:
for item in name:
if (item in LibcName) and ("amd64" in LibcName):
os.system("sudo cp ./libs/{}/* /glibc/{}/64/lib/".format(LibcName,item))
if (item in LibcName) and ("i386" in LibcName):
os.system("sudo cp ./libs/{}/* /glibc/{}/32/lib/".format(LibcName,item))
clibc
#!/bin/bash
FILE_NAME=$1
LIBC_VERSION=$2
WORKDIR=$(pwd)
LIBC_DIR=/glibc
LIBC_DIR=$(find $LIBC_DIR -name "$LIBC_VERSION*")
if [ "$LIBC_DIR" = "" ];then
echo "Not support version or your $LIBC_DIR don't have libc"
exit
fi
EBIT=$(file $FILE_NAME |awk '{print$3}'|cut -c 1-2)
if [ $EBIT -eq "32" ];then
libc_dir=$LIBC_DIR/32/lib
elif [ $EBIT -eq "64" ];then
libc_dir=$LIBC_DIR/64/lib
else
echo "It's not a elf file"
exit
fi
if [ "$3" ]
then
patchelf --set-interpreter $libc_dir/ld-$LIBC_VERSION.so --set-rpath $WORKDIR/ $1
else
patchelf --set-interpreter $libc_dir/ld-$LIBC_VERSION.so --set-rpath $libc_dir/ $1
fi
echo "success!!!"
clibc filename 2.23
上图可以看到用clibc修改之后(左图)和Ubuntu16自带的libc2.23加载空间没有什么区别
注:图中看到的ld-2.23.so是链接器