在ctf中共享库的问题仍然是一个十分特别十分难受的问题,这儿将会介绍动态链接库的命名以及怎样更改一个程序依赖的动态链接库
命名
0x7f387805e000 0x7f3878091000 r-xp 33000 0 /lib/x86_64-linux-gnu/libseccomp.so.2.4.3
主版本是重大升级,不会向下兼容(如python2和3)
次版本是增量升级linux动态链接库软件,添加一些新的插口
发布版本是进行一些错误修正,性能改进等
SO-NAME命名与软链接
用SO-NAME机制来记录共享库的依赖关系
每位库都有自己的SO-NAME著名库名子和主版本号如libc.so.2
SO-NAME相同的两个次版本号不同的库,次版本号大的兼容小的
在linux系统中,系统会为每位共享库在它所在的目录创建一个跟SO-NAME相同但是指向它的软联接如系统中存在共享库/lib/libfoo.so.2.6.1这么linux共享库管理程序会为它创建一个指向它的软链接/lib/libfoo.so.2
也有一些不标准的命名linux动态链接库软件,如libc.so.6还有ld-2.6.1.so被命名为ld-linux.so
SO-NAME的益处
编译时链接
在运行时须要链接一个程序可以加-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(假如不指定这个库就没有sonamelinux windows,用ldconfig也没用)
安装
包含两步1.创建SO-NAME软联接。2.告诉编译器和程序怎样查找共享库
共享库替换更改环境变量替换
LD_LIBRARY_PATH临时改变某个应用程序的共享库查找路径而不会影响系统中其他程序
类似于/lib/ld-linux.so.2-binary-path/home/user/bin/ls。
LD_PRELOAD优先级更高,无论程序是否依赖动态库,被指定的动态库就会被加载
系统将会从下边搜索库
更改二补码文件修改libc版本
有一个项目可以下载好多版本的libc
/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
为了完成前面的操作写了一个简单的脚本linux文件系统,完成更新列表,下载库,拷贝的过程
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
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是链接器
项目地址
/tower111/pwn-change-libc