CTF权威指南 笔记 -第二章二进制文件- 2.4 -动态链接

news2024/11/23 16:38:28

目录

静态文件的缺点

动态链接

位置无关代码

延迟绑定

_dl_runtime_reslove 函数定义

深入审视


静态文件的缺点

随着可执行文件的增加  静态链接带来的浪费空间问题就会愈发严重

 如果大部分可执行文件都需要glibc 那么在链接的时候就需要把 libc.a链接进去

如果一个libc.a为5M 那么100就是 5G  例如下面的左边

静态链接的一个很明显的缺点 对标准函数进行一点点 的修改 都需要重新编译整个源文件

动态链接

如果不把系统库和自己编写的代码链接到一个可执行文件 而是分割到两个独立的模块 等到程序进行运行了 再进行链接 这样就可以节省硬盘空间 并且在内存中一个系统库可以被多个程序使用 这样还会节省物理空间

在这种 运行和加载的时候 在内存中完成链接的过程叫做动态链接

这些用于动态链接的系统库 我们叫做共享库 或者 共享对象

这个过程我们通过 动态链接器完成

  例如上图的右边

func1.elf func2.elf 不再包含单独的testLib.o 
当程序运行func1.ELF的时候 系统将func1.o和依赖的testLib.o载入内存中
进行动态链接
完成后 系统将控制器交给程序入口点 

程序开始执行

后面func2.ELF开始执行 因为内存中已经有testLib.o 不需要重复加载 所以直接链接即可

继续使用之前静态链接的例子

这里我们把func.c编译为共享库

gcc -shared -fpic -o func.so func.c

-shared 生成共享库
-fpic 生成和位置无关的代码
gcc -fno-stack-protector -o func.ELF2 main.c  ./func.so


main.c 和func.so进行动态链接
生成 func.ELF2

我们查看 func.ELF2的链接格式

 

查看 汇编格式
objdump -d -M intel --section=.text func.ELF2 |grep -A 11 "<main>"

这里我们就能发现 call 后面跟上了偏移量 地址等

位置无关代码

可以加载而无需重定位的代码就是位置无关代码PIC

通过gcc -fpic就可以生成位置无关代码

一个程序的代码段和数据段的相对位置都是保持不变的   所以指令和变量之间的距离是一个运行时常量 与绝对内存地址无关  

于是就有了全局偏移量表 GOT

GOT表位于 数据段的开头
用于保存全局变量和库函数的引用

每个条目占8字节 在加载时会重定位并且填入符号的绝对地址
为了RELRO保护 
GOT 被拆分为 .got 和.got.plt

.got 用于保存全局变量引用 写入内存为只读  不需要延迟绑定

.got.plt 用于保存函数引用 具有读写权限  需要延迟绑定

我们可以看看func.o的情况

objdump -h func.so

readelf -r func.so| grep tmp

R_X86_64_GLOB_DAT 表示 动态链接器找到 tmp的值 存入0x3fd8

我们使用汇编可以看看代码

objdump -d -M intel --section=.text func.so |grep -A 20 "<func>"

 

 这里我们能发现 调用函数 rip+0x2ead的地方    rip为下一条指令的地址 这里是 112b

112b + 2ead =3fd8

所以汇编我们能发现调用函数就是指向 实际地址 3fd8

延迟绑定

如果动态链接是要通过动态链接器加载进行 如果要重定位的符号(库函数)多了之后 肯定会影响性能

延迟绑定就是为了解决这个问题

延迟绑定的思想就是

如果函数是第一次被调用 动态链接器才进行符号查找 重定位的操作
如果没有调用就不进行绑定

ELF文件通过 PLT和GOT的配合来实现延迟绑定

每一个被调用的库函数都有一组 GOT 和PLT

位于代码段.got.plt节 是一个PLT数组 每一个条目占16字节 
PLT[0] 跳转到动态链接器
PLT[1] 调用系统启动函数 _libc_start_main()函数
main函数就是从里面进行调用

PLT[2] 开始就被调用的各个函数条目
位于数据段.got.plt节的GOT也是一个数组  每条占8字节
GOT[0]和GOT[1]
包含着动态链接库解析函数地址所需要的两个地址 (dynamic和relor条目)

GOT[2] 是动态链接器 ld-linux.so的入口点 

GOT[3] 开始就是各个条目这些条目默认指向对应PLT条目的第二条指令
绑定后才会修改函数的实际地址

我们使用例子说明

#include<stdio.h>
void print_bananer(){
	printf("Welcome got and plt");
}
int main(void){
	print_bananer();
	return 0;
}

进行编译

gcc -Wall -g -o test.o -c test.c -m32


gcc -o test test.o -m32 -no-pie

我们直接通过反汇编进行查看

objdump -d test 

 

 我们先看到main函数

看到call

 11d8:	e8 c0 ff ff ff       	call   119d <print_bananer>
这里地址送11d8
e8为call的机器码

c0ffffff为printf_bananer的地址 这里第一次调用 不知道地址 所以用这个代替

119d是去的地址 指向 print_bananer函数

我们去看119d的地址

 发现119d是print_bananer的地址

然后我们看到里面的关键调用

11ba:	e8 91 fe ff ff       	call   1050 <printf@plt>
11ba地址   
e8 ->call
91 fe ff ff  为函数地址

我们去看看1050地址

我们发现是

printf.plt地址 就是

然后我们进行代码审计

这里我们可以通过另一个编译 让pie停止


gcc -o test test.o -m32 -no-pie

然后重新进行查看

objdump -d test 

发现一个地址 我们使用gdb进行查看这个地址

gdb test
b mian

r
x/x 0x804c010

这里是printf的got表

 这里得到另一个地址 我们回去反汇编看看

 发现就是下一条 push 0x8这个地址 所以第一个代码是返回 printf@plt的push

然后执行完push

又进行跳转

这里有一个地址 我们去看看

 

发现是main的共享库文件-0x10偏移量 的地址

然后进行push 0x804c004

又跳到0x804c008

gdb test 
x/x 0x804c008

我们发现 这里是0

因为第一次执行 还没有找到printf函数

我们让他执行起来

b main
r
x/x 0x804c008

发现得到了地址 这个地址就是 printf的地址

0x804c008这个的地址 其实就是指向 _dl_runtime_resolve函数

这个函数在动态链接里有着很重要的作用

计算出地址 更新got表等

我们现在来梳理一下

printf.plt ->printf.got.plt -> printf.plt->公共plt->_dl_runtime_reslove函数

这就是printf函数调用过程

这里我们还需要知道

_dl_runtime_reslove 怎么找到printf函数地址

这个是因为在之前我们执行printf@plt的时候 push了0x8

 这个就类似于 printf的id 让 _dl_runtime_reslove去找这个id的函数

_dl_runtime_reslove 函数定义

_dl_runtime_resolve(link_map_obj, reloc_index)

reloc_index 这个参数是来寻找 .rel.plt表的 这个表是ELF rel结构体

这个结构体为

typedef struct
{
    ELF64_Addr r_offset  
    ELF64_Xword r_info
}ELF64_Rel

r_offset是用于保存 需要进行重定位和重定位写入内存的位置

r_info的高三位是保存 符号在.dynsym里的下标

通过上面的图像 我们可以发现 r_info又指向 .dynsym中的ELF_sym结构体

这个结构体的内容为

typedef struct
{
    ELF64_Word st_name;   符号名称在字符串表中的偏移量
    unsigned char st_info;  包含符号类型和捆绑信息
    unsigned char st_other; 保留位
    ELF64_Half st_shndx; 指向和该符号关联的段/节索引值
    ELF64_Addr st_value; 该符号的值 就是地址
    ELF64_Xword st_size; 符号所占的字节
}ELF64_Sym

st_value和st_name为重要成员

st_value是符号导出时存放的虚拟地址 不能导出为null
st_name 是相对.dynstr的偏移

然后就是去.dynstr找函数

这个表中存放着 函数地址

所以我们从函数开始导入开始看

程序导入函数 .dynstr中增加一个函数名字符串
在.dynsym中增加一个ELF Sym的结构体 其中 S_name指向 .dynstr
然后又在.rel.plt中增加一个 ELF Rel结构体 其中 r_info指向 .dynsym的 ELF Sym结构体
最后 r_offset 构成 GOT表  存储在 .plt.got段的地址上

这样 我们函数就可以在程序的.plt.got中了

在我们明白这个了 我们重新对test进行审视

深入审视

这里第一个跳转 是无条件跳转到 GOT[0]处

但是GOT[0]在初始化的时候 默认指向 plt的第二条 就是PLT[1]->push 0x8

然后进行跳转到公共的PLT 然后去找_dl_runtime_reslove函数

这里的执行就是

无条件跳转到 xxx.got 把link_map_obj压入栈内
然后开始调回 plt 然后压入 函数id push 0x8
然后进行跳转去找_dl_runtime_reslove函数 并且执行

这样就实现了 函数通过动态链接寻找

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/489125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spark大数据处理讲课笔记3.6 RDD容错机制

文章目录 零、本讲学习目标一、RDD容错机制&#xff08;一&#xff09;血统方式&#xff08;二&#xff09;设置检查点方式 二、RDD检查点&#xff08;一&#xff09;RDD检查点机制&#xff08;二&#xff09;与RDD持久化的区别&#xff08;三&#xff09;RDD检查点案例演示 三…

Matlab 多项式拟合

一、线性 1、多项式 corrcoef函数 corrcoef函数用来计算矩阵相关系数。 (1)、corrcoef(x)&#xff1a;若x为一个矩阵&#xff0c;返回的则是一个相关系数矩阵。 (2)、corrcoef(x,y)&#xff1a;计算列向量x、y的相关系数&#xff0c;要求x、y具有相等的元素个数。如果x、y是矩…

2023河南土著双非硕士——毕业季秋招春招就业经验分享(仅限于在河南找工作,毕业想留河南)

作为一名河南土生土长的人&#xff0c;本硕皆就读于河南某双非一本&#xff0c;是一个实打实的河南土著&#xff0c;河南作为互联网就业的贫困环境&#xff0c;相较于CSDN博客上那么多动不动就腾讯、阿里、字节等大厂的就业经验分享&#xff0c;我更想分享一下我在河南省内找工…

百度文心一言正式亮相,数说故事受邀成为首批内测企业

3月16日下午&#xff0c;百度在北京召开新闻发布会&#xff0c;正式推出基于百度新一代大语言模型的生成式AI产品——文心一言&#xff0c;百度创始人、董事长兼首席执行官李彦宏现场展示了文心一言在文学创作、商业文案创作、数理推算、中文理解、多模态生成五个使用场景中的综…

卖期权的时候,我们在卖什么?

一直在思考一个问题&#xff0c;卖期权到底是怎么回事&#xff1f;卖实值期权、平值期权、虚值期权背后的本质有什么区别&#xff1f;卖近期的和远期的期权背后的本质又是什么?我们用沪深300指数期权来研究一下。 我们先从数据上来直观感受一下。上面这个表格是2020-12-09日这…

基于R语言的贝叶斯时空数据模型实践技术

时间&#xff0d;空间数据&#xff08;以下简称“时空数据”&#xff09;是最重要的观测数据形式之一&#xff0c;很多科学研究的数据都以时空数据的形式得以呈现&#xff0c;而科学研究目的可以归结为挖掘时空数据中的规律。另一方面&#xff0c;贝叶斯统计学作为与传统统计学…

【OpenCV】 2D-2D:对极几何算法原理

2D-2D匹配: 对极几何 SLAM十四讲笔记1 1.1 对极几何數學模型 考虑从两张图像上观测到了同一个3D点&#xff0c;如图所示**。**我们希望可以求解相机两个时刻的运动 R , t R,t R,t。 假设我们要求取两帧图像 I 1 , I 2 I_1,I_2 I1​,I2​之间的运动,设第一帧到第二帧的运动为…

MiniGPT-4部署过程

文章目录 项目背景部署过程环境配置与文件准备部署推理报错1报错2 项目背景 2023年4月19日&#xff0c;开源项目MiniGPT-4发布&#xff0c;该项目是由KAUST&#xff08;沙特阿卜杜拉国王科技大学&#xff09;&#xff0c;是几位博士开发的。 项目地址&#xff1a;https://gith…

Spark大数据处理讲课笔记3.4 理解RDD依赖

文章目录 零、本讲学习目标一、RDD依赖二、窄依赖&#xff08;一&#xff09;map()与filter()算子&#xff08;二&#xff09;union()算子&#xff08;三&#xff09;join()算子 三、宽依赖&#xff08;一&#xff09;groupBy()算子&#xff08;二&#xff09;join()算子&#…

字符设备驱动

字符设备就是按字节流进行读写的设备&#xff0c;读写数据分先后顺序&#xff0c;如点灯&#xff0c;IIC&#xff0c;SPI&#xff0c;LCD等都是字符设备&#xff0c;这些设备的驱动就叫字符设备驱动。 include/linux/fs.h中 file_operations 结构体为内核驱动操作函数集合&…

如何关闭tomcat?tomcat端口号被占用怎么办

我tomcat一跑就报被占用怎么办&#xff1f;我没开tomcat呀&#xff01;&#xff01; 这种情况一般是你上一次打开tomcat没有关tomcat服务就关闭了变成软件&#xff08;如强行关闭正在运行tomcat的idea&#xff09;&#xff0c;这样你在开tomcat就会显示端口号占用了&#xff0…

API 渗透测试从入门到精通系列文章(上)

导语&#xff1a;这是关于使用 Postman 进行渗透测试系列文章的第一部分。 这是关于使用 Postman 进行渗透测试系列文章的第一部分。我原本计划只发布一篇文章&#xff0c;但最后发现内容太多了&#xff0c;如果不把它分成几个部分的话&#xff0c;很可能会让读者不知所措。 所…

SMOKE Single-Stage Monocular 3D Object Detection via Keypoint Estimation 论文学习

论文地址&#xff1a;SMOKE: Single-Stage Monocular 3D Object Detection via Keypoint Estimation Github 地址&#xff1a;https://github.com/open-mmlab/mmdetection3d/tree/main/configs/smoke 1. 解决了什么问题&#xff1f; 预测物体的 3D 朝向角和平移距离对于自动驾…

hive之入门配置

学习hive之路就此开启啦&#xff0c;让我们共同努力 目录 Hive网站&#xff1a; Hive的安装部署&#xff1a; 启动并使用Hive&#xff1a; 安装Mysql: 安装Mysql依赖包&#xff1a; 启动Mysql: 查看密码&#xff1a; 登录root: 密码错误报错&#xff1a; 元数据库配置…

信创国产中间件概览

信创国产中间件概览 中间件国内中间件市场份额第一梯队仍然是IBM> 和Oracle&#xff0c;市场份额合计51%。第二梯队为五大国产厂商&#xff0c;包括东方通、普元信息、宝兰德、中创中间件、金蝶天燕&#xff0c;市场份额合计15%。东方通应用服务器TongWeb对标 开源&#xf…

人脸检测和行人检测3:Android实现人脸检测和行人检测检测(含源码,可实时检测)

人脸检测和行人检测3&#xff1a;Android实现人脸检测和行人检测检测(含源码&#xff0c;可实时检测) 目录 人脸检测和行人检测3&#xff1a;Android实现人脸检测和行人检测(含源码&#xff0c;可实时检测) 1. 前言 2. 人脸检测和行人检测数据集说明 3. 基于YOLOv5的人脸检…

Databend 开源周报第 91 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 新数据类型&…

【Robot Framework】RF关键字大全

收录工作当中最常用的Robot Framework关键字 内容较多&#xff0c;可以CtrlF快速搜索自己想要的 1. RF循环使用&#xff08;FOR循环&#xff09; {list1} create list LOG TXT INI INF C CPP JAVA JS CSS LRC H ASM S ASP FOR ${file_type} IN {list1} log 构造请求参数 ${t…

第二十二章 解释器模式

文章目录 前言一、解释器模式基本介绍解释器模式的原理类图 二、通过解释器模式来实现四则运算完整代码抽象表达式类 Expression变量表达式类 VarExpression抽象运算符号解析器 SymbolExpression加法解释器 AddExpression减法解释器 SubExpression计算器类 CalculatorClint 测试…

【C++】仅需一文速通继承

文章目录 1.继承的概念及定义继承的概念继承的定义定义格式:继承关系和访问限定符继承基类成员访问方式的变化 2.基类和派生类对象赋值转换3.继承中的作用域4.派生类的默认成员函数题目:设计出一个类A,让这个类不能被继承(继承了也没用) 5.继承与友元6.继承与静态成员7.复杂的菱…