Linklab实验

实验目的与要求

1. 理解链接的作用与工作步骤

2、掌握ELF结构、符号解析与重定位的工作过程

3、熟练使用binutils和hexedit等工具完成ELF分析与修改

实验仪器设备/实验环境

1.Linux操作系统 — 64位Ubuntu 18.04

2. gdb调试器和objdump反汇编指令、readelf和hexedit工具

3. 笔记本电脑

实验内容及步骤

题目分五关,每关都围绕main.o和phaseX.o操作,输出自己的学号。这些都是一些可重定位目标文件,满足ELF文件格式。

ELF头

段头部表:将连续的文件映射到运行时的内存段

. init :   定义了_init函数,程序初始化代码会调用它

. text :   已编译程序的机器代码

. rodata : 只读数据,比如printf语句中的格式串和开关语句的跳转表

. data :   已初始化的全局和静态C变量

. bss :    未初始化的全局和静态C变量

. symtab :一个符号表,它存放在程序中定义和引用的函数和全局变量的信息

. debug : 一个调试符号表,其条目时程序中定义的全局变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。

. line :   原始C源程序的行号和.text节中机器指令之间的映射

. strtab : 一个字符串表,其内容包括 .symtab 和 .debug节中的符号表,以及节头部中的节名字。

每个实验阶段(共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

$ 19210320303        [仅供示例,具体目标字符为每位学生学号]

实验注意事项

1.建议在linux下进行linklab文本编辑。

2.建议使用gdb或IDA之类的调试软件辅助进行。

3.实验前仔细阅读实验要求。

实验过程与结果

Phase1

运行看看有什么,发现是乱码

使用readelf -a phase1.o命令查看phase1.o的ELF数据

经过分析得知需要修改.data处的信息就可以输出自己的学号

先输出.data里面的东西看看是不是和一开始运行的东西一样

可以看到g_data的全局变量在[3]处,即.data处

用objdump -rd phase1.0看到.data用到的偏移量是0x7

即在.data的0x60+7=0x67处

先安装hexdit命令,因为乌班图没有

Hexdit常用的命令例举

用hexedit phase1.o命令来修改phase1.o,找到0x67处

从上图位置开始修改,输入自己的学号信息的ASCII码,最后加00

保存后退出用readelf -x .data phase1.o查看自己是否修改完成

然后重新链接运行就可以得到输出学号

Phase2

运行输出看看有什么,还是乱码

readelf -a phase2.o命令查看phase2.o的ELF数据

有个COM未被赋初始值,是个弱符号,根据文档提示打个补丁phase2_patch.o。

里面的内容应该是cahr类型的大小为256的g_myCharArray变量的初始化

用objdump -rd phase2.0看到g_myCharArray用到的偏移量是0x7

所以创建phase2_patch.c文件,创建一个g_myCharArray,偏移量是0x7,所以前7个填0,例如下图

然后在后面添加两个35测试一下输出结果,发现输出了*+,查看ASCII表发现了规律

然后添加到phase2_patch.c文件中,最后的字符结束位是-18,可参照上图规律自懂

重新链接运行后即可输出学号

Phase3

先导出phase3.o编译后的linkbomb3的汇编文件查看

根据编程思想要打印学号,那肯定打印在后,所以包含puts语句的myFunc1方法在后面,接收一个参数,这个参数应该是学号,myFunc2获取一个地址的值给

到%rax寄存器,那就是先调用myFunc2函数获取学号赋值给%rax,然后mov %rax,%rdi设置参数在调用myFunc1。需要注入的命令是
call myFunc2

mov %rax,%rdi

call myFunc1

call指令对应的机器码是e8 xx xx xx xx。

myFunc2的地址是0x40053e

根据call指令的特性,计算地址0x400559到0x40053e的偏移量是-1b即补码ff ff ff e5。小端法即

0x400554:e8 e5 ff ff ff

0x400559:这里是mov %rax,%rdi

mov %rax,%rdi的机器码是48 89 c7
所以即

0x400554:e8 e5 ff ff ff     (call myFunc2)

0x400559:48 89 c7        (movq %rax,%rdi)

0x40055c:e8 xx xx xx xx     (call myFunc1)

0x400561:(下一条指令的地址)

myFunc1的地址是0x400523

0x400561到0x400523的偏移量是-3e即补码ff ff ff c2

0x400554:e8 e6 ff ff ff     (call myFunc2)

0x400559:48 89 c7        (movq %rax,%rdi)

0x40055c:e8 c2 ff ff ff     (call myFunc1)

使用readelf -a phase3.o命令查看ELF数据,得到已编译程序的机器代码在

objdump -d phase3.o查看汇编代码

所以要注入的代码地址就是0x40+0x31=0x71

hexedit phase3.0修改

写入机器码

重新编译后查看汇编,确实写入了

现在输入学号到.data中,和之前步骤一样

用objdump -rd phase3.o查看到.data的偏移量还是0x7

用readelf -a phase3.o查看得到.data的地址是0x1A0

即0x1a0+0x7=0x1a7,hexedit phase3.o修改0x1a7后的值为学号,以”00”结尾

修改

重新编译链接运行得到输出学号

Phase4

先还是先编译并运行一下,发现报错了,然后对比汇编,看到重定位被修改了开头的四个数值

readelf -a phase4.o,查看phase4.o文件ELF数据

查看重定位表,需要修改三个地方

要修改这里的偏移量,从这里可以看出,一个是变量g_myCharArray一个是变量temp还有一个puts函数且g_myCharArray是位于.data节中偏移量为0(即value值)处,temp是位于.data节中偏移量为0x14(即value值)处

用objdump -d phase4.o查看汇编代码

观察可得,偏移量是要使变量和函数到这里即0x6、0x11、0x19

0x18是call指令,所以它对应的是call puts函数。要把这里的值设置为19

Hexedit phase4.0.rela.text段的开始为250

经过我的分析①是偏移量,②是信息,③是符号名称+加数的加数

这个是“第一组”的,以此类推可以找到”puts组”的位置,”FC FF FF FF FF FF FF”是-4的补码也验证了我们的计算

所以puts-4就是修改前面的内容就可以修改他的偏移量

重新查看汇编文件对比发现改对了,会发现变成了call puts

现在还有两个偏移量要修改

这两个是数据,看下面是这两个变量的值

用上面同样方法查看和计算插入学号的地址,得到下图插入学号到.data中

现在就要改对应的偏移量,就是上面说的汇编中0x6和0x11处

0x11应该放学号,因为要赋值给%rdi传递给puts函数打印,0x6应该放temp

“.data+10”组偏移量,修改为6

“.data+0”组偏移量,修改为11

查看修改后的

重新编译链接就可以输出学号了

Phase5

先编译链接运行后发现输出的是hahaha

然后查看汇编可知myFunc将函数是关键,主要做了一个判断,如果%rax为0则将0x60101040存入%edi并调用blankFunc函数;如果%rax为1则将0x60101030存入%edi并输出。

用gdb查看0x601030中保存的就是一开始输出的hahaha

本来想修改这来存储我们的学号拿来输出的,但是发现这数组的大小只有0x8,所以用看汇编,发现他还有用到一个数组的地址是0x601040,然后用gdb看来一下是g_myCharArray这个数组,查看他的elf发现是一个20字节的数组,可以用来存储我们的学号

从汇编中知道把%rax置为0就不会跳转,就得改变0x601038处的g_guard

可以看到.data是从0x90开始的

下图可以看到在g_myCharArray前面还有两个八字节的数组,所以往后退就可以知道输入学号的位置在哪里了

然后需要修改三个地方

  1. 将call指令调用的函数改成put函数(方法同上)
  2. 修改%rax的值为0不跳转
  3. 修改添加学号

保存后重新编译链接运行得到输出为学号

猜你喜欢

转载自blog.csdn.net/weixin_59151049/article/details/142976631