CSAPP LAB buflab-handout实验报告
文章目录
更好的阅读体验
一、实验目的:
加深对IA-32函数调用规则和栈帧结构的理解
二、实验要求
对目标程序实施缓冲区溢出攻击(buffer overflow attacks)
通过造成缓冲区溢出来破坏目标程序的栈帧结构
继而执行一些原来程序中没有的行为
三、实验内容(所修改函数代码,功能以及重要代码的解释):
Smoke
要求通过栈溢出攻击,使之跳到smoke函数。
文档给已经出了getbuf()、test()、smoke()的高级语言代码
老规矩,先objdump d bufbomb > bufbomb.asm
08049342 <getbuf>:
8049342: 55 push %ebp
8049343: 89 e5 mov %esp,%ebp
8049345: 83 ec 38 sub $0x38,%esp
8049348: 83 ec 0c sub $0xc,%esp
804934b: 8d 45 ce lea -0x32(%ebp),%eax //buf 缓冲区的大小为 0x32,即50个字节
804934e: 50 push %eax //eax里面就是缓冲区首地址
804934f: e8 ba fa ff ff call 8048e0e <Gets>
8049354: 83 c4 10 add $0x10,%esp
8049357: b8 01 00 00 00 mov $0x1,%eax
804935c: c9 leave
804935d: c3 ret
从 804934b 行指令可以看到 buf 缓冲区的大小为 50 个字节
08048ba1 <smoke>: //smoke 函数的地址为 0x08048ba1
8048ba1: 55 push %ebp
8048ba2: 89 e5 mov %esp,%ebp
8048ba4: 83 ec 08 sub $0x8,%esp
8048ba7: 83 ec 0c sub $0xc,%esp
8048baa: 68 30 a3 04 08 push $0x804a330
8048baf: e8 5c fd ff ff call 8048910 <puts@plt>
8048bb4: 83 c4 10 add $0x10,%esp
8048bb7: 83 ec 0c sub $0xc,%esp
8048bba: 6a 00 push $0x0
8048bbc: e8 fe 08 00 00 call 80494bf <validate>
8048bc1: 83 c4 10 add $0x10,%esp
8048bc4: 83 ec 0c sub $0xc,%esp
8048bc7: 6a 00 push $0x0
8048bc9: e8 52 fd ff ff call 8048920 <exit@plt>
smoke 函数的地址为 0x08048ba1
攻击字符串用来覆盖数组 buf,进而溢出并覆盖 ebp 和 ebp 上面的返回地址,攻击字符串的大小应该是 50+4+4=58 个字 节。攻击字符 串的最 后 4 字节 应是 smoke 函数 的地址0x08048ba1。
攻击字符串为:
成功
Fizz
Fizz传入了一个参数,文档告诉我们应该为cookie
08048bce <fizz>: //fizz 函数的地址为 0x08048bce
8048bce: 55 push %ebp
8048bcf: 89 e5 mov %esp,%ebp
8048bd1: 83 ec 08 sub $0x8,%esp
8048bd4: 8b 55 08 mov 0x8(%ebp),%edx //使用了一个参数
8048bd7: a1 38 d1 04 08 mov 0x804d138,%eax
8048bdc: 39 c2 cmp %eax,%edx
8048bde: 75 22 jne 8048c02 <fizz+0x34>
8048be0: 83 ec 08 sub $0x8,%esp
8048be3: ff 75 08 pushl 0x8(%ebp)
8048be6: 68 4b a3 04 08 push $0x804a34b
8048beb: e8 50 fc ff ff call 8048840 <printf@plt>
8048bf0: 83 c4 10 add $0x10,%esp
8048bf3: 83 ec 0c sub $0xc,%esp
8048bf6: 6a 01 push $0x1
8048bf8: e8 c2 08 00 00 call 80494bf <validate>
8048bfd: 83 c4 10 add $0x10,%esp
8048c00: eb 13 jmp 8048c15 <fizz+0x47>
8048c02: 83 ec 08 sub $0x8,%esp
8048c05: ff 75 08 pushl 0x8(%ebp)
8048c08: 68 6c a3 04 08 push $0x804a36c
8048c0d: e8 2e fc ff ff call 8048840 <printf@plt>
8048c12: 83 c4 10 add $0x10,%esp
8048c15: 83 ec 0c sub $0xc,%esp
8048c18: 6a 00 push $0x0
8048c1a: e8 01 fd ff ff call 8048920 <exit@plt>
所以可以这样构造攻击串:50(冲掉 buf)+4(冲掉 ebp 旧值)+4(用 fizz 函数地址08048bce替换,冲掉返回地址)+4(占位置)+4(这里到了ebp+8的位置,即需要的参数,把 cookie 值按照小端方式放到这里就行)
我的 cookie 是 0x25ed4761
成功
Bang
要求进入bang()函数,并且修改global_value的值为cookie
08048c1f <bang>: //返回地址0x08048c1f
8048c1f: 55 push %ebp
8048c20: 89 e5 mov %esp,%ebp
8048c22: 83 ec 08 sub $0x8,%esp
8048c25: a1 40 d1 04 08 mov 0x804d140,%eax
8048c2a: 89 c2 mov %eax,%edx
8048c2c: a1 38 d1 04 08 mov 0x804d138,%eax //比较0x804d138和0x804d140里面的内容,根据文档可知,要将global_value==cookie
8048c31: 39 c2 cmp %eax,%edx
8048c33: 75 25 jne 8048c5a <bang+0x3b>//要他们相同,gdb调试知804d140是global_value
8048c35: a1 40 d1 04 08 mov 0x804d140,%eax
8048c3a: 83 ec 08 sub $0x8,%esp //movl $0x804d140,804d138//将cookie赋值给全局变量
8048c3d: 50 push %eax
8048c3e: 68 8c a3 04 08 push $0x804a38c //push $0x08048c1f //函数bang的地址压入栈
8048c43: e8 f8 fb ff ff call 8048840 <printf@plt> //ret
8048c48: 83 c4 10 add $0x10,%esp
8048c4b: 83 ec 0c sub $0xc,%esp
8048c4e: 6a 02 push $0x2
8048c50: e8 6a 08 00 00 call 80494bf <validate>
8048c55: 83 c4 10 add $0x10,%esp
8048c58: eb 16 jmp 8048c70 <bang+0x51>
8048c5a: a1 40 d1 04 08 mov 0x804d140,%eax
8048c5f: 83 ec 08 sub $0x8,%esp
8048c62: 50 push %eax
8048c63: 68 b1 a3 04 08 push $0x804a3b1
8048c68: e8 d3 fb ff ff call 8048840 <printf@plt>
8048c6d: 83 c4 10 add $0x10,%esp
8048c70: 83 ec 0c sub $0xc,%esp
8048c73: 6a 00 push $0x0
8048c75: e8 a6 fc ff ff call 8048920 <exit@plt>
bang的地址08048c1f
gdb调试看看global_value的地址在哪里
是0x804d140
编写攻击汇编代码(作用就是把 cookie 放到 0x804d1a0 处,然后调用 bang 函数),并进行反汇编得到机器码
那么,怎么才能调用到这段函数呢?
可以把它写入 buf 数组中,buf 数组会被加载进入缓冲区,找到缓冲区的地址,用它覆盖掉返回地址就行了。
因为
804934b: 8d 45 ce lea -0x32(%ebp),%eax
804934e: 50 push %eax
通过调试,可以找到 eax 为 0x5568317e,就是缓冲区首地址
成功
Bomb
前面的攻击都是使目标程序跳转到特定函数,进而利用exit函数结束目标程序运行,在这个过程中我们都把原来的恢复现场需要用的返回地址和原test的ebp给破坏了。Bomb这一关中,我们将修复这些被我们破坏的栈状态信息,让最后还是回到test中,让被攻击者不容易发现我们动了手脚,
另外,构造攻击字符串,使得getbuf都能将正确的cookie值返回给test函数,而不是返回值1。设置返回值也就是更改eax(eax中保存的就是函数的返回值)的值,可以用mov指令设置eax存的为cookie值。更改完要进入test函数继续执行下面的指令,也就是下图中这个位置,将这个地址压栈。
8048c88: e8 b5 06 00 00 call 8049342 <getbuf>
8048c8d: 89 45 f4 mov %eax,-0xc(%ebp)
我们需要构造一串攻击指令,ret之前push 的地址是返回地址,是 call getbuf 行的 pc,也就是 8048c8d。
把它写到缓冲区中,并用缓冲区地址替换返回地址,构造出下图所示的攻击串
恶意代码准备好了,将返回地址更改为指向这个代码的位置,把恶意代码放到buf缓冲区内,返回地址和bang一样。
接下来要恢复ebp的值,先得到ebp旧值。
gdb调试:在getbuf第一行,push ebp 设置断点。查看ebp的值。用这个值覆盖ebp值。ebp值在返回地址的低方位,放在返回地址前。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KiMmgk2u-1654332656850)(C:\Users\26012\AppData\Roaming\Typora\typora-user-images\image-20211226111555640.png)]
ok
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6FY1avt6-1654332656850)(C:\Users\26012\AppData\Roaming\Typora\typora-user-images\image-20211226111651674.png)]
Nitro
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qazV3Khq-1654332656850)(C:\Users\26012\AppData\Roaming\Typora\typora-user-images\image-20211226111849067.png)]
这一关难度比前面都大。但也是承接上一关的,构造攻击字符串使getbufn函数,返回cookie值至testn函数,而不是返回值1,需要将cookie值设为函数返回值,复原被破坏的栈帧结构,并正确地返回到testn函数。但这一关与之前最大的不同在于地址空间随机化,每次攻击,被攻击函数的栈帧内存地址都不同,也就是函数的栈帧位置每次运行时都不一样,不能准确地跳转到栈空间的某个特定地址。因此,要想办法保证每次都能够正确复原原栈帧被破坏的状态,使程序每次都能够正确返回。
0804935e <getbufn>:
804935e: 55 push %ebp
804935f: 89 e5 mov %esp,%ebp
8049361: 81 ec c8 02 00 00 sub $0x2c8,%esp
8049367: 83 ec 0c sub $0xc,%esp
804936a: 8d 85 3e fd ff ff lea -0x2c2(%ebp),%eax //706
8049370: 50 push %eax
8049371: e8 98 fa ff ff call 8048e0e <Gets>
8049376: 83 c4 10 add $0x10,%esp
8049379: b8 01 00 00 00 mov $0x1,%eax
804937e: c9 leave
804937f: c3 ret
在这一关buffersize也从50增大到了706,这个做法是有意义的。
大概的思路是,虽然栈的初始地址不同,但会在一些范围里浮动,所以我们需要把我们的代码填在706字节的最后几个字节里,并且前面全面的空间都填上nop(编码为0x90
)。不管我们跳转到哪个nop,最后都会执行到我们的代码。
8048cff: 89 45 f0 mov %eax,-0x10(%ebp)
8048d02: e8 57 06 00 00 call 804935e <getbufn> //ebp = esp + 0x18
8048d07: 89 45 f4 mov %eax,-0xc(%ebp)
ebp是随机的,但是ebp相对esp是绝对的,根据上面的图中结合栈结构得出
ebp = esp + 0x18
getbufn函数返回后要从8048d07开始执行,将这个地址压栈。
设置cookie给eax。
综合得出恶意代码为:
因为随机,不知道程序会跳到哪里,所以把恶意代码放到最后面,用nop滑行。
(nop不会执行任何操作,只有PC加一,机器码是90。)
所以,前面塞满90,最后面写上恶意代码程序,以及最后要跳的位置。
寻找要跳的地址:
由于随机化,buf首地址不确定。用gdb调试:在0x8049370设断点,看eax的值。(每执行一次,要用c命令继续,进而执行下一次)
如此这样五次,注意这次调试时运行r需要加入-n
取最高地址0x55682eee作为返回地址,这样就会一路滑行到恶意代码,执行恶意代码。
完成!
四、实验总结
下一次)
如此这样五次,注意这次调试时运行r需要加入-n
[外链图片转存中…(img-NeRtSnyl-1654332656851)]
[外链图片转存中…(img-hPoLbU3w-1654332656851)]
[外链图片转存中…(img-x5ivf2tj-1654332656851)]
取最高地址0x55682eee作为返回地址,这样就会一路滑行到恶意代码,执行恶意代码。
[外链图片转存中…(img-lwOXzTxo-1654332656851)]
完成!
[外链图片转存中…(img-km65ngQP-1654332656852)]
四、实验总结
学到了很多东西,加深了对栈溢出、函数调用、寻址的理解。