前置条件:阅读完第三章
实验准备
在官网下载 handout
并解压到本地文件夹,仔细阅读 Writeup
文档,文档中详细介绍了实验的做法,需要用到的东西等内容。
于是运行命令:
由文档知,ctarget
为第一个任务,rtarget
为第二个任务,其中 farm.c
是 rtarget
才需要用到的,hex2raw
是一个可执行文件,能将十六进制数转为二进制。
新建一个文本文件,命名为 ans.txt
,我们可以通过命令:
来得到运行结果。若没有参数 -q
则会出现 Error
,因为你没有 CSAPP
的账号,不能发送到服务器上去……(你没资格啊,你没资格啊——)
实验过程
ctarget
我们所需要做的,就是在调用 getbuf
函数返回时,不返回到 test
函数,而返回到 touch_
函数。
并且需要使用的攻击方式是注入代码 / 字节,使缓冲区溢出。
touch1
缓冲区溢出攻击
通过缓冲区溢出,修改返回的地址,使控制权发生更改到指定的位置
touch1
很简单,我们可以阅读 getbuf
部分的汇编可以发现:
getbuf
分配了 个字节的栈帧(在 sub $0x28, %rsp
中得出)。于是,为了返回时跳转到 touch1
函数,我们只需要先填满 getbuf
的栈帧,然后将返回处的地址改为 touch1
的地址。我们可以从 touch1
的汇编得到其地址:
也就是 0x4017c0
。于是答案为:
注意地址需要用小端法书写(在第二章中有提到 x86-64 的机器都是用小端法)
touch2
这里的 cookie
在解压出的文件 cookie.txt
已经给出,于是,我们需要的就是在调用 touch2
时,传入一个参数 val
使其与 cookie
相等。
我们将此数值传递给寄存器 %rdi
,然后将返回地址修改为 touch2
的入口地址即可,但由于我们只能使用 ret
来转移控制权,因此我们需要兜个圈子来达到这一目的。
注意到,从
stdin
中得到的字符串,存储在函数getbuf
中的%rsp
所指向的内存中
所以我们可以:
- 将移交命令写入缓冲区
buf
中 - 将返回地址修改为
%rsp
的值
这样,在 ret
后,pc
所拿到的地址就是缓冲区的地址,就可以执行我们实现写入的指令了。
指令可以写为:
注意到这里,我们使用 push
与 ret
来完成控制移交,这里的 push
其实等价于:
而 ret
等价于:
这样,我们通过 gcc -c func.s
与 objdump -d func.o
后,可以得到指令序列:
48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3
显然不够 字节,剩下的按 0 补即可。
第二步,我们需要将 getbuf
函数的返回地址更改为 %rsp
的值也就是栈顶,通过 gdb
我们可以得到:
其值为 0x5561dc78
于是,我们可以写出指令序列为:
touch3
与 touch2
的任务类似,但我们需要传入的是 cookie
的字符串形式,而非一个立即数,因此,我们需要查询 0x59b997fa
的 ascii
形式怎么表示(不需要 0x
)
其字符串表示为 : 35 39 62 39 39 37 66 61 00
(注意,我们需要在末尾加上 '\0'
我们可以轻松写出如下汇编:
需要解决的问题实际上只有一个,HELLO_WORLD
到底应该存在什么位置(也就是指针应该是多少)
考虑 touch2
,我们显然可以把这个指针设置成 %rsp
的偏移量,由于我们知道 %rsp
的值,那么就很轻松能够算出来这个指针了。于是,我们的机器码如下:
注意这里的 0x5561dca8
就是字符串的指针,计算方式为 %rsp + 0x30
(由于buf
占据 个字节, 而返回地址占 字节,占 字节的原因是需要对齐,因此总共占据 字节)
rtarget
touch2
显然,我们需要找到 cookie
存放的位置,并将其移动到 %rdi
之中,然后调用 ret
来跳转到 touch2
的位置,我们可以将 cookie
的值置于栈顶,然后通过 pop
指令弹出到一个寄存器中(如果可以直接弹出到 %rdi
的话就完美了,否则我们还需要一次 mov
指令)
查表可以发现,无法在 farm
中找到 5f
,但可以找到 58
(也只有 58
),于是,我们可以找到如下指令:
即
分别位于:0x4019ab
与 0x4019a2
注意,我们不能选择
0x4019b0
位置的那个序列,因为我们需要保证执行完这个指令后,下一条指令一定是c3
(i.e.retq
),这样才能保证我们的控制链是连贯的
于是,我们根据之前的计划,安排好次序:
由于我们最后一次需要跳转到 touch2
,并且需要填充 buf
的 字节,于是,完整的代码为:
touch3
难点主要在于如何传递这个字符串指针,在前面我们知道,字符串的指针是通过 %rsp
算出来的,但由于这里我们没办法知道 %rsp
的绝对值,所以我们必须读取 %rsp
的值,并对其做偏移
movq %rsp, %rax
,机器码为48 89 e0 c3
add $offset, %rax
movq %rax, %rdi
,机器码为48 89 c7 c3
此时,这个 offset
我们只能在给我们的代码中寻找,可以发现这个函数 addval_190
,其汇编解释为:
这里给予我们一些启发:如果这个 0x37
能够被加到 %rax
上就好了
通过一些探索,我们可以得到:
add $0x37, %al
的机器码就是 04 37
(探索也很容易,就是不断缩小 %rax
的位数,反正只需要偏移 0x37
,小一点也没关系)
那么,我们的字符串指针就需要在 %rsp + 0x37
的位置,也就是需要偏移 55
个字节,那么答案如下: