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),%edx
  17:   83 ec 0c                sub    $0xc,%esp
  1a:   52                      push   %edx
  1b:   89 c3                   mov    %eax,%ebx
  1d:   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   %ebp
  42:   89 e5                   mov    %esp,%ebp
  44:   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   %ebp
 5b6:	89 e5                	mov    %esp,%ebp
 5b8:	53                   	push   %ebx
 5b9:	83 ec 04             	sub    $0x4,%esp
 5bc:	e8 9f fe ff ff       	call   460 <__x86.get_pc_thunk.bx>
 5c1:	81 c3 13 1a 00 00    	add    $0x1a13,%ebx
 5c7:	83 ec 08             	sub    $0x8,%esp
 5ca:	8d 83 50 e7 ff ff    	lea    -0x18b0(%ebx),%eax
 5d0:	50                   	push   %eax
 5d1:	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),%eax
   6:	50                   	push   %eax
   7:	e8 a6 ff ff ff       	call   0xffffffb2
   c:	58                   	pop    %eax

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

phase3

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

 643:	8d 55 e9             	lea    -0x17(%ebp),%edx
 646:	8b 45 e4             	mov    -0x1c(%ebp),%eax
 649:	01 d0                	add    %edx,%eax
 64b:	0f b6 00             	movzbl (%eax),%eax
 64e:	0f b6 c0             	movzbl %al,%eax
 651:	8d 93 70 00 00 00    	lea    0x70(%ebx),%edx
 657:	0f b6 04 02          	movzbl (%edx,%eax,1),%eax
 65b:	0f be c0             	movsbl %al,%eax
 65e:	83 ec 0c             	sub    $0xc,%esp
 661:	50                   	push   %eax
 662:	e8 e9 fd ff ff       	call   450 <putchar@plt>
 667:	83 c4 10             	add    $0x10,%esp
 66a:	83 45 e4 01          	addl   $0x1,-0x1c(%ebp)
 66e:	8b 45 e4             	mov    -0x1c(%ebp),%eax
 671:	83 f8 09             	cmp    $0x9,%eax
 674:	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和动态链接有关。

  • 3
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值