【发布时间】:2012-03-20 19:38:09
【问题描述】:
我阅读了一些关于堆栈缓冲区溢出的文章,例如this,并了解了攻击者如何通过覆盖函数指针来利用堆栈缓冲区溢出漏洞。然后我写了一个小程序来演示一个攻击:
#include <stdio.h>
#include <string.h>
void fun1 ( char * input ) {
char buffer[10];
strcpy( buffer, input );
printf( "In fun1, buffer= %s\n", buffer );
}
void fun2 ( void ) {
printf ( "HELLO fun2!\n" );
}
int main ( int argc, char * argv[] )
{
printf ( "Address of fun2: %p\n", fun2 );
fun1( "abcdefghijklmnopqrstuv\x52\x84\x04\x08" );
return 0;
}
该程序使用 GCC 4.5.1 在 Fedora 14 x86 下编译。下面是输出:
$ ./exp01
fun2的地址:0x8048452
在 fun1 中,buffer=abcdefghijklmnopqrstuvR...
你好 fun2!
你好 fun2!
我们可以看到 fun2() 被成功调用了,但是我不知道为什么它运行了两次。然后我对它进行了 GDBed(见下文)。 (我只知道一些关于GDB的基本说明╮( ̄▽ ̄)╭)
我google了一些关键词,比如“__libc_csu_fini()”,但没有找到一个清晰的方法可以帮助我理解程序的执行路径。我对编译器和进程的内部结构知之甚少,所以我认为我可能必须找到一些详细描述这些东西的书籍或文章。有什么建议吗?谢谢!
GDB 记录:
(gdb) 列表
7 printf("in fun1, buffer= %s\n", buffer);
8 }
9
10 void fun2 ( void ) {
11 printf ( "HELLO fun2!\n" );
12 }
13
14 int main (int argc, char * argv[])
15 {
16 printf("fun2 的地址:%p\n", fun2);
(gdb)
17 fun1("abcdefghijklmnopqrstuv\x52\x84\x04\x08");
18 返回 0;
19 }
(gdb) 中断 16
0x804846f 处的断点 1:文件 hello.c,第 16 行。
(gdb) 运行
启动程序:/home/yuliang/test/hello
断点 1,主要 (argc=1, argv=0xbffff394) 在 hello.c:16
16 printf("fun2 的地址:%p\n", fun2);
缺少单独的调试信息,使用:debuginfo-install glibc-2.13-2.i686
(gdb) 步骤
fun2的地址:0x8048452
17 fun1("abcdefghijklmnopqrstuv\x52\x84\x04\x08");
(gdb)
fun1 (input=0x804859a "abcdefghijklmnopqrstuvR\204\004\b") at hello.c:6
6 strcpy(缓冲区,输入);
(gdb)
7 printf("in fun1, buffer= %s\n", buffer);
(gdb)
在 fun1 中,buffer=abcdefghijklmnopqrstuvR...
8 }
(gdb)
fun2 () at hello.c:10
10 void fun2 ( void ) {
(gdb)
11 printf ( "HELLO fun2!\n" );
(gdb)
你好 fun2!
12 }
(gdb)
__libc_csu_fini()中的0x08048500
(gdb)
单步执行直到退出函数 __libc_csu_fini,
没有行号信息。
fun2 () at hello.c:10
10 void fun2 ( void ) {
(gdb)
11 printf ( "HELLO fun2!\n" );
(gdb)
你好 fun2!
12 }
(gdb)
无法访问地址 0x76757477 处的内存
(gdb)
单步执行直到退出函数 __libc_csu_init,
没有行号信息。
0x009aae36 in __libc_start_main () from /lib/libc.so.6
(gdb)
单步执行直到退出函数 __libc_start_main,
没有行号信息。
程序以代码 0241 退出。
(gdb)
【问题讨论】:
-
当你在 fun1 中不使用 printf 运行程序时,程序也会执行两次吗?
-
您需要使用机器代码调试器来单步调试这些东西 --- 查找 gdb nexti、stepi 和 disas 命令。 C 模式下的调试器会变得非常混乱,因为它依赖于有效的堆栈帧才能知道在哪里执行了什么,当然它们不再是了,因为您刚刚更改了它们。
-
感谢您的回复@Azrael3000。我注释掉 fun2 中的 printf。它回到 main() 中的 printf,并进入死循环。但是 fun2() 的地址现在是 0x804843e。如果我调用 fun1("abcdefghijklmnopqrstuv\x3e\x84\x04\x08") 而不是 fun1("abcdefghijklmnopqrstuv\x52\x84\x04\x08"),fun2 仍然运行两次。
-
非常有用的信息。我真的需要更多地了解 gdb。谢谢@DavidGiven
-
我同意大卫的观点。调试器将依赖于拥有一个有效的堆栈指针并从那里展开堆栈。
标签: c buffer-overflow