CSAPP Link总结;HIT linkbomb实验记录

2024-04-19 14:48

本文主要是介绍CSAPP Link总结;HIT linkbomb实验记录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录结构:
一、链接总结
(1)符号解析:1、符号定义分类 2、静态链接解析过程 3、符号表条目
(2)重定位
(3)动态链接 1、静态库的缺点 2、位置无关代码 3、数据和代码调用
二、HIT linkbomb实验记录

链接总结

链接的作用是合并多个可重定位目标文件与静态/动态库,分为两个步骤,符号解析and重定位。
符号解析后获得(1)要合并的.o文件的集合(2)需要确定地址的符号集合
重定位分三部分(1)同节合并(2)确定地址(3)修改引用

一、符号解析
目的:将每个模块引用的符号与目标模块定义的符号相关联
1、符号定义分类:
符号定义分为三种:
(1)全局符号:指模块内部定义,并能被外部引用的符号;
全局符号有强弱之分,函数名和已初始化的全局变量名是强符号,未初始化的全局变量名是弱符号。
(2)外部符号:其他模块定义,在本模块引用的符号,对应符号定义的弱符号,
(3)局部符号:本模块带static说明的符号

void func(){} //强符号func
void swap() ; //弱符号swap
int  main(){}

多重符号处理规则:
(1)强符号不能重复定义
(2)一个符号被多次定义弱符号和一次强符号,则以强符号为准
(3)多个弱符号定义,任选其中一个;可以gcc -fno-common,来对这种情况输出警告信息

2、静态链接符号解析过程:
E: 所有合并.o文件的集合
U: 未解析符合集合
D: 已解析符号集合
1、按照命令行顺序扫描.o和.a文件
2、扫描过程将未解析的引用放入U
3、每遇到一个.o或.a,都试图去解析U中的符号
4、如果扫描到最后,U集合还未变空,则链接失败

注意必须将可重定位目标文件写在静态库前边,原因是如果颠倒次序,首先扫描静态库,而此时U为空,因此静态库中的.o文件不会加入E。而之后的.o文件中的引用符号无法找到其在静态库文件中的定义,U集合无法变空,因此导致链接错误。
如何创建静态库?———ar rs x.a \ 1.o 2.o …

符号解析的结果是
(1)E集合:.o文件集合,.o文件中含有重定位条目(记录符号位置、符号名、重定位类型),可用readelf -r x.o查看
(2)D集合:需要重定位符号集合

3、符号表条目
使用readelf -s命令可查看ELF可执行或可重定位文件的符号表
符号表中会显示:
(1)Num: 序号
(2)Value: 在对应节中的偏移量
(3)Size: 大小
(4)Type: 三种情况
1、FUNC:函数
2、OBJECT:全局变量
3、FILE:源文件的名字
4、SECTION:
(5) Bind: 两种情况:
1、bind=LOCAL:符号只能在本地使用,如static修饰的函数或变量
2、bind=CLOBAL(全局符号) :表示符号在本模块定义,但是可以被其它模块引用,如extern修饰的全局变量和函数
(6)Vis:
(7)Ndx:三种情况:
1、UND:本模块引用,但非本模块定义的符号
2、ABS:表示不需要被链接器处理,一般是文件名
3、COM:未分配空间的对象,如未被初始化的全局变量
4、数字:表示节索引号
(8)Name:符号名

二、重定位
在符号解析后获得集合E(.o文件集合)和符号集合D,接下来进入重定位阶段
重定位阶段会做三件事:
(1)同节合并 ----> 针对集合E,将多个.o中相同的节合并
(2)确定地址 ----> 针对集合D,确定D中符号的地址
(3)修改引用 ----> 针对引用符号,将确定好的地址添加到符号引用处,需要借助存放在.rel_data和.rel_text的重定位信息(汇编器会为每个引用生成)

基本重定位类型:
(1)绝对地址重定位,填入目标的绝对地址
(2)PC相对地址重定位,填入目标的相对偏移

三、动态链接

1、动态链接的优点是什么?
静态库的缺点:
(1)因为很多可执行文件都会合并相同的静态库.o文件,因此造成磁盘存储空间浪费,运行时内存资源浪费
(2)如果静态库函数有更新,则所有可执行文件都需要重新编译链接,十分麻烦。

针对静态库的缺点,当前OS很少使用静态库,转而使用共享库(Linux .so文件,Windows .dll文件),共享库在磁盘和内存中只有一个备份,可以在装入并运行时动态加载并链接。
共享库的创建方式:gcc -shared -fPIC
PIC: 位置无关代码,表示共享库代码的位置可以是不确定的

2、加载时动态链接过程:
gcc -o myproc main.o ./mylib.so(标准C库libc.so不用显式指出)
(1)先会调用静态链接器,生成部分链接的可执行目标文件(因为静态链接器链接的是.o和.so文件,所以并不会把共享库中的内容直接合并)
(2)文件调用加载器(execve),加载器会根据.interp节中的动态链接器地址,启动动态链接器
在这里插入图片描述
(3)动态链接器将需要链接的内容从共享库拷贝到存储空间,不占用磁盘空间

3、位置无关代码
什么是位置无关代码?— 简单说就是共享库中的代码,因为链接对象不同,合并过去的代码和数据地址是不能确定的,因此在创建动态库的时候,必需加 -fPIC选项生成位置无关代码。

4、动态链接对代码和数据的引用
(1)模块内函数调用 :直接偏移量寻址
(2)模块内数据调用:比静态链接复杂,因为无法确定链接后的地址,数据也不能通过偏移访问。因此必须动态获取当前地址,方式是通过call获取下一条指令的地址,加上已知的偏移。
(3)模块外数据调用:方法是在data节起始处构建GOT(GLOBAL_OFFSET_TABLE),然后重定位由动态链接器填入对应的值,通过固定的偏移访问(方法同模块内数据调用类似)。
(4)模块外函数调用:应用PLT(过程链接表),PLT位于.text节开始处,每项代表一个共享库函数,函数地址有动态链接器重定位填写,调用处直接call PLT的地址,PLT会跳到对应的GOT…再经过一系列跳转重定位…转到目标函数。

哈工大ICS-linkbomb实验

PPT及实验资料地址:https://github.com/Tory123/CSAPP/tree/master/linklab

实验分成五个phase,每个phase需要将main和对应的phase文件链接,修改可执行文件或可重定位目标文件。

phase1

定位字符串在.data中的偏移,然后在HexEdit中修改就可以了。
readelf -s phase1.o 查看phase1.o符号表:
在这里插入图片描述
这个奇怪的符号名应该就是字符串名。。。TYPE为OBJECT说明是全局变量,Size为200,Value=0,表示节内偏移为0。
接下来只需要查看data节在.o文件中的偏移是多少应该就可了,readelf -S phase1.o
在这里插入图片描述
Off = 80,打开HexEdit,定位 ALJsLxmF
在这里插入图片描述
不修改直接链接输出的话是这样的:
在这里插入图片描述
可见并不是从ALJsLxmF变量开始打印的,应该有一个偏移,其实现在直接在HexEdit中修改对应位置就可以了,但不妨反汇编看下:

  11:   8d 90 11 00 00 00       lea    0x11(%eax),%edx17:   83 ec 0c                sub    $0xc,%esp1a:   52                      push   %edx1b:   89 c3                   mov    %eax,%ebx1d:   e8 fc ff ff ff          call   1e <do_phase+0x1e>

lea 0x11(%eax),%edx,可见加了偏移0x11,数数个数,第18个字符正好是开始输出的位置,接下来直接修改为自己的学号,链接输出:
在这里插入图片描述

phase2

readelf -s phase2.o 找到自己的输出函数,实验的求解可以分为两部分:
(1)如何寻访MYID,是phase2求解的关键
(2)使用HexEdit修改phase2.o

1、如何寻访MYID?
链接之前MYID存放在phase2.o的rodata节中,链接之后在MYID地址不确定,因此无法直接压栈传参。这里需要了解动态链接模块内数据的访问方式,因为是动态链接,所以模块内数据访问是通过GOT来实现的,反汇编phase2.o:

00000041 <do_phase>:41:   55                      push   %ebp42:   89 e5                   mov    %esp,%ebp44:   e8 fc ff ff ff          call   45 <do_phase+0x4>49:   05 01 00 00 00          add    $0x1,%eax

实际上最后两句的功能就是将%eax指向GOT,那么接下来就是确定MYID在GOT中的偏移,可以在LLnDkLJR函数中查看,因为LLnDkLJR也需要访问GOT获取MYID的地址。
将未修改的phase2.o和main.o链接为可执行文件,反汇编查看:

000005b5 <yeDfwUkv>:5b5:	55                   	push   %ebp5b6:	89 e5                	mov    %esp,%ebp5b8:	53                   	push   %ebx5b9:	83 ec 04             	sub    $0x4,%esp5bc:	e8 9f fe ff ff       	call   460 <__x86.get_pc_thunk.bx>5c1:	81 c3 13 1a 00 00    	add    $0x1a13,%ebx5c7:	83 ec 08             	sub    $0x8,%esp5ca:	8d 83 50 e7 ff ff    	lea    -0x18b0(%ebx),%eax5d0:	50                   	push   %eax5d1:	ff 75 08             	pushl  0x8(%ebp)5d4:	e8 07 fe ff ff       	call   3e0 <strcmp@plt>

lea -0x18b0(%ebx),%eax,说明MYID在可执行文件中的偏移为-0x18b0

2、使用HexEdit修改phase2.o实现学号输出

lea -0x18b0(%eax), %eax
push %eax
call -86
pop %eax

gcc -c 生成.o文件反汇编查看机器码:

00000000 <.text>:0:	8d 80 50 e7 ff ff    	lea    -0x18b0(%eax),%eax6:	50                   	push   %eax7:	e8 a6 ff ff ff       	call   0xffffffb2c:	58                   	pop    %eax

在do_phase中添加,执行:
在这里插入图片描述

phase3

cookie字符串定义在栈中-0x17(%ebp)处,%eax记录循环次数,学号为9位数,循环9次每次打印一个字符。
objdump定位循环结构:

 643:	8d 55 e9             	lea    -0x17(%ebp),%edx646:	8b 45 e4             	mov    -0x1c(%ebp),%eax649:	01 d0                	add    %edx,%eax64b:	0f b6 00             	movzbl (%eax),%eax64e:	0f b6 c0             	movzbl %al,%eax651:	8d 93 70 00 00 00    	lea    0x70(%ebx),%edx657:	0f b6 04 02          	movzbl (%edx,%eax,1),%eax65b:	0f be c0             	movsbl %al,%eax65e:	83 ec 0c             	sub    $0xc,%esp661:	50                   	push   %eax662:	e8 e9 fd ff ff       	call   450 <putchar@plt>667:	83 c4 10             	add    $0x10,%esp66a:	83 45 e4 01          	addl   $0x1,-0x1c(%ebp)66e:	8b 45 e4             	mov    -0x1c(%ebp),%eax671:	83 f8 09             	cmp    $0x9,%eax674:	76 cd                	jbe    643 <do_phase+0x3e>

使用readelf -s phase3.o命令查看符号表
在这里插入图片描述

第十一项为即为数组名称,cDBDohBAOo,Size=256,COM表示未初始化
一开始题目没看懂,以为在phase3_patch.c中直接重新把学号赋值给cDBDohBAOo就可以了,没看清是映射数组…
因此phase3的关键就在于了解打印的是cDBDohBAOo数组中哪几个字符,需要通过查看内存获取cookie存储的值:

(gdb) x/s   0xffffd081
0xffffd081:	"astpqrwhbf"

cookie存储的是字符,需要查看字符的ascii值,才能获取在cDBDohBAOo映射的位置。
ascii码对应关系如下:
’a‘:97 ->1
’s‘:115 ->1
’t‘:116 ->7
’p‘: 112 ->3
’q‘: 113 ->0
‘r’:114 ->0
‘w’:119 ->0
‘h’:104 ->2
‘b’:98 ->0
‘f’:102 ->9
因此可构造强符号cDBDohBAOo,覆盖原来的弱符号:
共120个字符,98-120是访问的范围
phase3_patch.c:

char  cDBDohBAOo[256] = "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110111912111111130017110";

编译链接,成功输出:
在这里插入图片描述

phase4:

phase4和phase3类似,通过一个十位的数组获取跳转表映射,输出内容.
分析反汇编,cookie定义在栈中-0x17(%ebp)处,查看内存获取数组中的值:

(gdb) x/s  0xffffd061
0xffffd061:	"BGOMEIUFQJ"

对应26个英文字母,跳转表共有26个表项,由“BGOMEIUFQJ”序列可知访问跳转表表项的顺序为:2 7 15 13 5 9 21 6 17 10,接下来要做的就是修改跳转表对应表项的值。
通过反汇编可以获取跳转表表项在ELF文件中的偏移,在HexEdit中直接修改即可。
由于时间原因只改了前三个,原理都是一样的
在这里插入图片描述
尝试直接修改phase4.o,跳转表的内容在phase4.o的.rel.rodata和.symtab节中,.rel.rodata存储需重定位的只读数据,.symtab和动态链接有关。

这篇关于CSAPP Link总结;HIT linkbomb实验记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/917843

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

整数Hash散列总结

方法:    step1  :线性探测  step2 散列   当 h(k)位置已经存储有元素的时候,依次探查(h(k)+i) mod S, i=1,2,3…,直到找到空的存储单元为止。其中,S为 数组长度。 HDU 1496   a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 。 x在 [-100,100] 解的个数  const int MaxN = 3000

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

go基础知识归纳总结

无缓冲的 channel 和有缓冲的 channel 的区别? 在 Go 语言中,channel 是用来在 goroutines 之间传递数据的主要机制。它们有两种类型:无缓冲的 channel 和有缓冲的 channel。 无缓冲的 channel 行为:无缓冲的 channel 是一种同步的通信方式,发送和接收必须同时发生。如果一个 goroutine 试图通过无缓冲 channel

记录每次更新到仓库 —— Git 学习笔记 10

记录每次更新到仓库 文章目录 文件的状态三个区域检查当前文件状态跟踪新文件取消跟踪(un-tracking)文件重新跟踪(re-tracking)文件暂存已修改文件忽略某些文件查看已暂存和未暂存的修改提交更新跳过暂存区删除文件移动文件参考资料 咱们接着很多天以前的 取得Git仓库 这篇文章继续说。 文件的状态 不管是通过哪种方法,现在我们已经有了一个仓库,并从这个仓

9.8javaweb项目总结

1.主界面用户信息显示 登录成功后,将用户信息存储在记录在 localStorage中,然后进入界面之前通过js来渲染主界面 存储用户信息 将用户信息渲染在主界面上,并且头像设置跳转,到个人资料界面 这里数据库中还没有设置相关信息 2.模糊查找 检测输入框是否有变更,有的话调用方法,进行查找 发送检测请求,然后接收的时候设置最多显示四个类似的搜索结果