实验四 LinkLab实验

1 篇文章 0 订阅
1 篇文章 0 订阅

一、实验目的

  1. 了解链接的基本概念和链接过程所要完成的任务。
  2. 理解ELF目标代码和目标代码文件的基本概念和基本构成
  3. 了解ELF可重定位目标文件和可执行目标文件的差别。
  4. 理解符号表中包含的全局符号、外部符号和本地符号的定义。
  5. 理解符号解析的目的和功能以及进行符号解析的过程。

二、实验仪器设备/实验环境

Linux Ubuntu 18.04 64-bit

笔记本电脑

VMware虚拟机

三、实验内容

每个实验阶段(共5个)考察ELF文件组成与程序链接过程的不同方面知识

阶段1:全局变量ó数据节

阶段2:强符号与弱符号ó数据节

阶段3:代码节修改

阶段4:代码与重定位位置 

阶段5:代码与重定位类型

在实验中的每一阶段n(n=1,2,3,4,5…),按照阶段的目标要求修改相应可重定位二进制目标模块phase[n].o后,使用如下命令生成可执行程序linkbomb:

$ gcc -o linkbomb main.o phase[n].o 

正确性验证:如下运行可执行程序linkbomb,应输出符合各阶段期望的字符串: 

$ ./linkbomb

$ 23215150438    

实验结果:将修改后正确完成相应功能的各阶段模块(phase1.o, phase2.o, …)提交供评分。 

四、实验步骤

1. 实验数据

学生实验数据包: linklab学号.tar

数据包中包含下面文件:

main.o:主程序的二进制可重定位目标模块(实验中无需修改)

phase1.o, phase2.o, phase3.o, phase4.o, phase5.o:各阶段实验所针对的二进制可重定位目标模块,需在相应实验阶段中予以修改。

2. 实验工具

readelf:读取ELF格式的各.o二进制模块文件中的各类信息,如节(节名、偏移量及其中数据等)、符号表、字符串表、重定位记录等 

objdump:反汇编代码节中指令并提供上述部分类似功能 

hexedit:编辑二进制文件内容 

五、实验阶段

1、phase1

1、执行 readelf -a phase1.o,查找有关的输出函数的内容

 puts 函数的参数是 g_data ,其重定向类型是绝对地址( R_X86_64_32 ),其加数为 0x11 。我们只需要将 g_data 的内容修改为自己的学号字符串就可以了

再往下看:查看 g_data 符号所在节GLOBAL可以得出是全局变量,所以我们就去看它的.data

 

得出要看.data,查看数据节在全文中的偏移量,所以 .data 节的偏移量为 0x60

然后我们再加上前面0x11;

得出g_data 在全文中的具体位置:0x60+0x11=0x71;

再将学号转换为对应的ascii码值:

如:我的学号是23215150438 对应的是32 33 32 31 35 31 35 30 34 33 38

得出数据后:

用hexedit phase1.o命令来修改phase1.o,并对phase1.o数据节中相应字节进行修改

 

光标停在要修改的数字,输入要修改的数字即可;修改完成后使用快捷键ctrl + w实现保存操作,随后通过快捷键ctrl + x实现退出操作

链接并查看输出结果

执行 gcc -o linkbomb main.o phase1.o -no-pie

执行 ./linkbomb

如果正确则会输出正确的学号

2、phase2

输入命令:readelf -a phase2.o,查看函数相关的输出内容

 

put 函数的参数是 g_myCharArray ,其加数为 0x11 

接着往下看:

COM 表示 g_myCharArray 是一个未初始化的弱符号数组,其大小为 256 ,所以我们需要创建一个已初始化强符号的 g_myCharArray 来覆盖弱符号。

然后我们创建

phase2_patch.c文件并写入0x11个字节的“0”以及学号ASCII码。

0x32,0x33,0x32,0x31,0x35,0x31,0x35,0x30,0x34,0x33,0x38

保存运行下面三条命令:

gcc -c phase2_patch.c
gcc -o linkbomb2 main.o phase2.o phase2_patch.o -no-pie
./linkbomb2

 编译出来后发现每个字符都被偏移了。

 然后为了方便计算输入的结果,我们编写一个程序来手动计算偏移量得出的结果

hack.c

 然后运行:gcc hack.c -o hack

gcc -o linkbomb2 main.o phase2.o phase2_patch.o -no-pie

./linkbomb2 > out

./hack < out

输入完后就会出现如下:

 接着我们修改phase2_patch.c文件的内容

 接着运行我们的命令即可编译通过。

gcc -c phase2_patch.c
gcc -o linkbomb2 main.o phase2.o phase2_patch.o -no-pie
./linkbomb2

3、phase3

首先先链接 linkbomb3 执行:gcc -o linkbomb3 main.o phase3.o -no-pie

使用 objdump -d linkbomb3 查看:

从中看出,就是先调用myFunc2函数获取学号赋值给%rax,然后mov %rax,%rdi设置参数在调用myFunc1

所以我们注入的命令顺序为:

1、call myFunc2
2、Mov %rax,%rdi
3、Call myFunc1

所以我们找到 myFunc2 中的内存位置修改为学号。

使用 readelf -a phase3.o 查看 .text 节的偏移量:为0x40

然后再使用 objdump -d phase3.o 查看 do_phase 填充代码地址

所以 填充的起始地址是 0x40 + 0x31 = 0x71。

其中 call 指令是相对寻址,myFunc 函数的地址为

第一条指令call指令对应的机器码是e8 xx xx xx xx ,占 5 个字节,结束地址为 0x31 + 0x5 = 0x36,所以距离 myFunc2 函数地址的相对距离为 0x1b - 0x36 = e5 ff ff ff,

所以第一条 call 指令为 e8 e5 ff ff ff。

第二条指令mov %rax,%rdi,mov %rax,%rdi的机器码是为 48 89 c7

第三条指令为call指令对应的机器码是e8 xx xx xx xx ,占 5 个字节,加上第二条指令的 3 个字节,所以结束地址为 0x36 + 0x3 + 0x5 = 0x3e ,所以距离 myFunc1 函数地址的相对距离为 0x0 - 0x3e = c2 ff ff ff ,所以第三条指令为 e8 c2 ff ff ff 。

修改 phase3.o 的二进制,将构造的代码注入

然后执行 hexedit phase3.o 从 0x71 开始写入我们构造的代码

接着来查看是否修改
执行 objdump -d phase3.o查看 do_phase3 函数:发现修改成功。

 查看全局符号,查找 myFunc2 获取的数据的内存地址
执行 readelf -a phase3.o 查看重定位符号:data+11

 查看 .data 节的起始地址:

 所以我们需要修改为学号的位置为 0x1a0 + 0x11= 0x1b1 。

执行 hexedit phase3.o 从0x1b1修改成我们的学号:32 33 32 31 35 31 35 30 34 33 38

 

 链接并运行

执行 gcc -o linkbomb3 main.o phase3.o -no-pie

./linkbomb3

 最后可以看到我们成功输出学号了。

4、phase4

输入:readelf -a phase4.o,以此来查看我们 phase4.o 的 elf 文件,然后发现了异常块:

发现我们的.text的重定向节符号的偏移量都为0,这明显就不正常,所以我们就要从这里下手。

 接着我们再查看 phase4.o 的汇编代码,查看留空位置。
执行 objdump -d phase4.o

 按文本位置,我们可以得出偏移量为: 0x6; 0x11; 0x19

查看重定向 .text 节初始地址,修改偏移量。

 所以 .rela.text 的初始地址为 0x250。修改偏移量:

执行 hexedit phase4.o 从 0x250修改偏移量;

 

 查看修改结果,看到修改成为了我们想要的偏移量了,

 接着修改我们的学号
按文本顺序 .data + 0x0 的位置即参数位置。查看 .data 节的起始地址:

 执行 hexedit phase4.o 从0x60修改成我们的学号:32 33 32 31 35 31 35 30 34 33 38

 

 最后链接并运行程序

执行gcc -o linkbomb4 main.o phase4.o -no-pie

./linkbomb4

 然后我们发现学号输出不全,缺少了2。

然后我们就去查看符号表:发现发现 temp 的偏移量应该为 0x14 

 修改 phase4.o 将 .data + 0x14 位置(也就是0x74的位置)的值修改为 0x00 。

 最后重新链接并运行,即可输出成功我们的学号了。

5、phase5 

链接并调试 linkbomb5
执行 gcc -o linkbomb5 main.o phase5.o -no-pie

查看汇编代码:objdump -d phase5.o

 进行gdb调试我们的linkbomb5:

执行:gdb ./linkbomb5

设置断点:b do_phase

 运行:r

打开调试窗口layout asm,layout regs

 然后设置断点进入到我们的myFunc;输入命令:b myFunc

 查看发现g_guard等于1
然后我们输出一下:x/s 0x601030,下面会走到一个假的数组,这并不是我们想要的

 接着再输出:x/s 0x601040,然后就出现了我们想要的字符

 接着查看两个数据的重定向信息执行 readelf -a phase5.o

即我们将两个偏移量交换。从上图可知重定向的初始地址为 0x340 

执行 hexedit phase5.o 修改 phase5.o 文件

重新链接并运行

执行 gcc -o linkbomb5 main.o phase5.o -no-pie

 

 readelf -a phase5.o查找数据地址,修改内容为学号:32 33 32 31 35 31 35 30 34 33 38

 修改过后我们需要的字符串为 .data + 0x10 的位置,查看 .data 节初始地址:

所以数据存放位置为 0x90 + 0x10 = 0xa0。

修改 phase5.o 文件

链接并运行程序

执行 gcc -o linkbomb5 main.o phase5.o -no-pie

 

 成功输出学号。

六、实验总结

对于课程的内容有了进一步的加深,但是对于一些指令还是不够熟悉,今后会更加认真的进行进一步的系统学习。

  • 53
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值