实验四 LinkLab实验 |
您所在的位置:网站首页 › 第三阶段共3个阶段配置 › 实验四 LinkLab实验 |
一、实验目的
了解链接的基本概念和链接过程所要完成的任务。理解ELF目标代码和目标代码文件的基本概念和基本构成了解ELF可重定位目标文件和可执行目标文件的差别。理解符号表中包含的全局符号、外部符号和本地符号的定义。理解符号解析的目的和功能以及进行符号解析的过程。
二、实验仪器设备/实验环境
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、phase11、执行 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
成功输出学号。 六、实验总结对于课程的内容有了进一步的加深,但是对于一些指令还是不够熟悉,今后会更加认真的进行进一步的系统学习。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |