CSAPP - LinkLab实验(阶段1-5)

LinkLab实验(阶段1-5)

官网:http://csapp.cs.cmu.edu/3e/labs.html

实验内容

每个实验阶段(共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. 实验数据
    学生实验数据包: linklab学号.tar
    数据包中包含下面文件:
    main.o:主程序的二进制可重定位目标模块(实验中无需修改)
    phase1.o, phase2.o, phase3.o, phase4.o, phase5.o:各阶段实验所针对的二进制可重定位目标模块,需在相应实验阶段中予以修改。
    解压命令:tar xvf linklab学号.tar
  2. 实验工具
    readelf:读取ELF格式的各.o二进制模块文件中的各类信息,如节(节名、偏移量及其中数据等)、符号表、字符串表、重定位记录等
    objdump:反汇编代码节中指令并提供上述部分类似功能
    hexedit:编辑二进制文件内容

实验过程

目标:修改目标文件,使其输出学号。

阶段 1

  1. 使用 readelf 查看 phase1.o,查找有关输出函数的内容

    执行 readelf -a phase1.o
    在这里插入图片描述
    我们可以看出 puts 函数的参数是 g_data ,其重定向类型是绝对地址( R_X86_64_32 ),其加数为 0x50 。我们只需要将 g_data 的内容修改为学号字符串,即可完成阶段一。

  2. 查看 g_data 符号所在节
    在这里插入图片描述

    OBJECT 说明 g_data 是一个全局变量,所以它应该在数据节 .data 中。

  3. 查看数据节在全文中的偏移量
    在这里插入图片描述

    所以 .data 节在全文的偏移量为 0x60

  4. 计算 g_data 在全文中的具体位置

    根据计算公式
    在这里插入图片描述

    可得 g_data 所在位置为 0x60+0x50=0xb0

  5. 使用 hexedit 修改 g_data 为学号并保存
    在这里插入图片描述

  6. 链接并查看输出结果

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

    执行 ./linkbomb1
    在这里插入图片描述

    成功输出学号。

阶段 2

  1. 使用 readelf 查看 phase2.o,查找有关输出函数的内容
    在这里插入图片描述

    从中可以看出 put 函数的参数是 g_myCharArray ,其加数为 0x50 ,重定向类型为绝对地址。

  2. 查看符号表
    在这里插入图片描述

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

  3. 创建 phase2_match.c 文件
    在这里插入图片描述

    加数为 0x50 字节需要填充。

  4. 编译 phase2_match.c 并链接,运行程序

    编译:运行 gcc -c phase2_match.c 生成 phase2_match.o 目标文件。

    链接:运行 gcc -o linkbomb2 main.o phase2.o phase2_match.o -no-pie

    运行 ./linkbomb2
    在这里插入图片描述

    发现每个字符都被偏移了。为了方便我们编写一个程序来计算应该输入的结果,可手动计算偏移量得出结果。

  5. 编写 crack.c 文件计算反偏移的字符串
    在这里插入图片描述

  6. 修改 phase2_match.c 中的学号为基准值
    在这里插入图片描述

  7. 编译 crack.c ,链接 linkbomb2,将 linkbomb2 的结果导出为 out,使用 out 运行 crack。

    编译 crack.c :运行 gcc crack.c -o crack

    编译 phase2_match.c :运行 gcc -c phase2_match.c

    链接 linkbomb2 :运行 gcc -o linkbomb2 main.o phase2.o phase2_match.o -no-pie

    运行 linkbomb2 并将结果导出为 out :./linkbomb2 > out

    运行 crack 程序 : ./crack < out
    在这里插入图片描述

  8. 将结果覆盖 phase2_match.c 中的字符串
    在这里插入图片描述

  9. 编译链接运行 linkbomb2

    编译 phase2_match.c :运行 gcc -c phase2_match.c

    链接 linkbomb2 :运行 gcc -o linkbomb2 main.o phase2.o phase2_match.o -no-pie

    运行 linkbomb2 :运行 ./linkbomb2
    在这里插入图片描述

    成功输出学号。

阶段 3

  1. 链接 linkbomb3 了解程序运行流程

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

    使用 gdb 进行调试
    在这里插入图片描述

    发现程序流程并没有执行 puts 函数,而是执行了一个叫 do_phase3 的函数,所以我们需要查找可利用的代码,来完善 do_phase3 函数。

  2. 查看 linkbomb3 中可利用的代码

    使用 objdump -d linkbomb3 查看:
    在这里插入图片描述

    从中看出,myFunc2 函数的作用是从内存中取出某值放到 %rax 中,myFunc1 函数的作用是以 %rdi 为参数执行 puts 函数。所以我们的目标是在 do_phase3 中 call myFunc2 输出我们的学号。而我们可以修改的值在 myFunc1 ,所以我们的流程为:在 do_phase3 中按顺序写下

    call myFunc2
    mov %rax,%rdi
    call myFunc1
    

    然后找到 myFunc2 中的内存位置修改为学号。

  3. 查找源代码文本位置
    使用 readelf -a phase3.o 查看 .text 节的偏移量
    在这里插入图片描述
    使用 objdump -d phase3.o 查看 do_phase 填充代码地址
    在这里插入图片描述
    所以 90 填充的起始地址是 0x40 + 0x31 = 0x71。

  4. 构造写入代码
    我们要构造的代码为

    call myFunc2
    mov %rax,%rdi
    call myFunc1 
    

    其中 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。
    第二条指令为 48 89 c7
    第三条指令为 e8 xx xx xx xx ,占 5 个字节,加上第二条指令的 3 个字节,所以结束地址为 0x36 + 0x3 + 0x5 = 0x3e ,所以距离 myFunc1 函数地址的相对距离为 0x0 - 0x3e = c2 ff ff ff ,所以第三条指令为 e8 c2 ff ff ff 。

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

    执行 hexedit phase3.o 从 0x71 开始写入我们构造的代码
    在这里插入图片描述

  6. 查看是否修改
    执行 objdump -d phase3.o查看 do_phase3 函数:
    在这里插入图片描述
    成功修改源码。

  7. 查看全局符号,查找 myFunc2 获取的数据的内存地址
    执行 readelf -a phase3.o 查看重定位符号 在这里插入图片描述
    所以数据从 .data 节+ 0x50 的位置获取的。查看 .data 节的起始地址:
    在这里插入图片描述
    所以我们需要修改为学号的位置为 0x1a0 + 0x50 = 0x1f0 。

  8. 修改 phase3.o
    在这里插入图片描述

  9. 链接并运行
    执行 gcc -o linkbomb3 main.o phase3.o -no-pie
    在这里插入图片描述
    成功输出学号。

阶段 4

  1. 查看 phase4.o 的 elf 文件,查看异常块
    在这里插入图片描述
    发现 .text 的重定向节符号的偏移量都为 0 ,很明显不会出现多个符号覆盖同一地址的操作。所以这三个符号的偏移量缺失了。

  2. 查看 phase4.o 的汇编代码,查看留空位置。
    执行 objdump -d phase4.o
    在这里插入图片描述
    按文本位置,从上到下偏移量为 0x4 + 0x2 = 0x6 ,0xf + 0x2 = 0x11 ,0x18 + 0x1 = 0x19 。

  3. 查看重定向 .text 节初始地址,修改偏移量。
    在这里插入图片描述
    所以 .rela.text 的初始地址为 0x250。修改偏移量:
    在这里插入图片描述

  4. 查看修改
    在这里插入图片描述
    成功修改。

  5. 修改 puts 函数的参数为学号
    按文本顺序 .data + 0x0 的位置即参数位置。查看 .data 节的起始地址:在这里插入图片描述
    修改 phase4.o 文件在这里插入图片描述

  6. 链接并运行程序

    执行gcc -o linkbomb4 main.o phase4.o -no-pie
    在这里插入图片描述
    发现输出不全。

    我们使用 gdb 进行调试
    在这里插入图片描述
    发现 temp 的值为 0x4d 。并非 .data + 0x10 位置上的值。

    我们查看符号表:
    在这里插入图片描述
    发现 temp 的偏移量应该为 0x14 。

  7. 修改 phase4.o 将 .data + 0x14 位置的值修改为 0x00 。在这里插入图片描述

  8. 重新链接并运行

    执行 gcc -o linkbomb4 main.o phase4.o -no-pie
    在这里插入图片描述
    成功输出学号。

阶段 5

  1. 链接并调试 linkbomb5
    执行 gcc -o linkbomb5 main.o phase5.o -no-pie在这里插入图片描述
    查看值:
    在这里插入图片描述
    发现这里是一个假的字符串,我们查看上面 0x404030 地址的值:在这里插入图片描述
    发现这才是我们需要字符串。

  2. 查看两个数据的重定向信息在这里插入图片描述
    我们将两个偏移量交换。从上图可知重定向的初始地址为 0x340 。

  3. 修改 phase5.o 文件在这里插入图片描述

  4. 重新链接并运行

    执行 gcc -o linkbomb5 main.o phase5.o -no-pie
    在这里插入图片描述

  5. 查找数据地址,修改内容为学号在这里插入图片描述
    修改过后我们需要的字符串为 .data + 0x10 的位置,查看 .data 节初始地址:在这里插入图片描述
    所以数据存放位置为 0x90 + 0x10 = 0xa0。

  6. 修改 phase5.o 文件在这里插入图片描述

  7. 链接并运行程序

    执行 gcc -o linkbomb5 main.o phase5.o -no-pie
    在这里插入图片描述
    成功输出学号。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霖行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值