TOC
Open TOC
Link Lab
源
https://www.icourse163.org/course/NJU-1449521162
MOOC 的版本似乎只有四个阶段,而原版似乎有六个阶段……
编译选项
注意加上 -m32 选项
不如写个 makefile
main
readelf
objdump
main.o 中会调用相应模块 phase[n].o 中如下定义的一个全局函数指针变量 phase
其中,作为其初始值的 do_phase 是各阶段模块中实现的一个全局函数,用来完成该阶段的具体功能
所以似乎现在 main 没啥用
phase1 - 静态数据与 ELF 数据节
修改 .data section 的内容
readelf
objdump
这一阶段比较简单,先介绍暴力方法,先观察默认的输出:
然后定位 phase1.o 中这段字符串的位置,起始在 0xd9,暴力修改为:
即 0809NJU064,即可……
查看和修改二进制文件:
然后切换到十六进制模式:
修改完后切换回来
最后保存即可
下面介绍正常的方法,注意到 IA-32 使用栈来传递参数,我们定位 phase1.o 的反汇编
可以传递给 puts 函数的参数为 0x79
至于为什么是 puts 函数,观察 .rel.text 知在 .text section 偏移 0x10 的地方有一个重定位表项,其名字为 puts
猜测
fc ff ff ff
是为了在重定位时弥补 PC + 4IA-32 和 x86-64 似乎有略微不同
而 .data section 在 ELF 文件中的偏移为 0x60,两者相加立得 0xd9
可以通过反汇编可执行文件 linkbomb 进一步观察:
phase2 - 指令与 ELF 代码节
修改 .text section 的内容
readelf
objdump
我们需要在 do_phase 中填充一些指令,使其调用 AFPQyOcF 函数
nndZqWCYPw 函数与本阶段无关
通过对照 .rel.text 或反汇编可执行文件,可知 AFPQyOcF 函数的行为如下:
其中字符串 yKSPHJa
通过下面三条指令可以推断出来:
同样是对照 .rel.text 或反汇编可执行文件,可知这里 call 的是 strcmp,第一个参数为调用 AFPQyOcF 的第一个参数,第二个参数对照 .rel.text 可知在 .rodata 中,偏移为 0x2,我们查看 .rodata 的开头:
可知为 79 4b 53 50 48 4a 61 00
,即 yKSPHJa
或者通过 gdbgui 直接调试可执行文件也可知字符串地址在 0x804a0e6
处,为 yKSPHJa
同样可以分析出打印的字符串为调用 AFPQyOcF 的第二个参数
我们来复习一下 IA-32 的过程调用:
在被调用过程中,调用过程的第一个参数在 0x8(%ebp),第二个参数在 0xc(%ebp) 以此类推
为此我们需要构造如下的函数调用:
也就需要构造 do_phase 的栈帧,如图所示:
我们编写汇编代码:
注意构造参数时为 leal 而非 movl,因为是地址
通过 gcc -m32 -c temp.S 可得:
注意到偏移为 -(0xd0 - 0xa0 + 3b) = 0xffffff95
,对应修改 phase.o 即可
最终 phase.o 的 do_phase 反汇编如下:
phase3 - switch 语句与重定位
不允许修改 .text section 和 relocation section 的内容
readelf
Relocation section
objdump
call 的函数为 puts
经过调试可知 COOKIE 字符串为 TPDFOCYAZS,进行的变换如下
对目标字符串下标为 i 的字符,在下面的地址处
会有一个地址,该地址处会有一条 movb 语句,其操作数即为该字符
注意到 0x41 即为 A,为此,我们需要修改跳转表
注意到 .rel.rodata 的存在,且从第 1 个表项开始(从 0 计数),反映了这种间接寻址
调试可知第 1 个表项 b7 对应实际地址 0x804926d,即 movb $0x4a,-0x9(%ebp)
发现 b7 就是在 .text 中的偏移
找到一个对应关系,我们就能推导出所有的映射
cookie | offset | item | addr | string |
---|---|---|---|---|
T | 19 | bd → 96 | 30 | 0 |
P | 15 | 96 → bd | 38 | 8 |
D | 3 | ff → 96 | 30 | 0 |
F | 5 | d5 → ff | 39 | 9 |
O | 14 | ed → c3 | 4e | N |
C | 2 | db → b7 | 4a | J |
Y | 24 | 105 → 111 | 55 | U |
A | 0 | b7 → 96 | 30 | 0 |
Z | 25 | cf → 7b | 36 | 6 |
S | 18 | b1 → c9 | 34 | 4 |
参考 1
参考 2
对应修改即可
phase4 - 可重定位目标文件
修改 relocation section 的内容
readelf
Section Headers
Relocation section
代码框架
.rel.text 缺失 6 个,.rel.data 缺失 1 个
CMKbeL 对应 CODE_TRAN_ARRAY
未缺失
缺失 70 / 90 / a4
vvWaVk 对应 CODE
未缺失
KvXOBb 对应 DICT
未缺失
OdZAJybs 对应 BUFFER
未缺失
puts
对应
encoder
对应
strlen
对应
注意下面两个没有出现在 relocation section 中,可以通过范围来判断:
还有
这是 for 循环初始化循环变量
不要以为操作数是
00 00 00 00
就需要重定位
综上,.rel.text 剩下的缺失如下:
这里应该对应 CODE,即 vvWaVk,为调用 transform_code 准备第一个参数
这里则是调用 transform_code
这里则是调用 generate_code
分析完 .rel.text 后,我们研究一下重定位表项的数据结构
/usr/include/elf.h
定位 .rel.text 的二进制表示
先补充前三个缺失 70 / 90 / a4
第四个
下面两个是 PC 相对寻址,r_info 部分高 24 位是符号索引,低 8 位是重定位类型
看一看符号表
举个例子:
0x13 → 19 → strlen
所以第五个为 11a 0f02,第六个为 250 1002
最后一个在 .rel.data
看一下 .data
猜测在偏移 0x3c 处为 encode_1,即 3c 1201
但是最后的输出似乎和实验指南中不一致:
算了,不想再看了……