- 论坛徽章:
- 13
|
回复 13# _nosay - #include <stdio.h>
- struct s_fun {
- void (*fun)(int, int);
- };
- struct s_target {
- unsigned int target;
- };
- void fun(int m, int n)
- {
- printf("m = %d, n = %d\n", m, n);
- }
- void target()
- {
- printf("hello\n");
- #if 0
- __asm__ __volatile__(
- "popl %%ebx\n\t"
- "popl %%ebx\n\t"
- :::"ebx"
- );
- #endif
- }
- // __cdecl__ default
- int /*__attribute__((__stdcall__))*/ main()
- {
- struct s_fun f[1] = { {fun} };
- struct s_target t = { 0x08048462 }; // objdump -d
- printf("%p, %p\n", &f[1], &t);
- printf("0x%x, 0x%x\n", sizeof(f[1]), sizeof(t));
- printf("0x%x\n", (unsigned int)f[0].fun);
- printf("0x%x, 0x%x\n", (unsigned int)f[1].fun, ((struct s_target*)&f[1])->target);
- f[0].fun(0, 0);
- f[1].fun(1, 1); // overflow to target()
- return 0;
- }
复制代码 这个程序用于模拟“利用溢出执行指定目标代码”的效果,即f[1].fun(1,1)实际上执行的是target(),打印结果如下:
[121] $ ./a.out
0xbfb1a4cc, 0xbfb1a4cc
0x4, 0x4
0x8048440
0x8048462
0x8048462
m = 0, n = 0
hello
但实验过程中我产生了一个疑问:由于调用时传了2个参数,肯定是压到栈里去了,但并没什么什么地方进行堆栈平衡处理,为什么程序没有coredump?
我最先想到的是linux函数调用默认采用的是__cdecl__方式,gcc会在f[1].fun(1,1)调用之后自动加上出栈的指令,但指定main()调用方式为__stdcall__,程序仍然正常结束了。。
后来看了一下main()函数反汇编的结果是这样的:- 8048476: 55 push %ebp
- 8048477: 89 e5 mov %esp,%ebp
- 8048479: 83 e4 f0 and $0xfffffff0,%esp
- 804847c: 83 ec 20 sub $0x20,%esp // 虽然只有8字节的局部变量,但分配了0x20大小的栈帧
- ...
- 80484ff: 8b 44 24 18 mov 0x18(%esp),%eax
- 8048503: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) // 参数0,不会覆盖局部变量
- 804850a: 00
- 804850b: c7 04 24 00 00 00 00 movl $0x0,(%esp) // 参数0,不会覆盖局部变量
- 8048512: ff d0 call *%eax // 调用f[1].fun(0, 0)
- 8048514: 8b 44 24 1c mov 0x1c(%esp),%eax
- 8048518: c7 44 24 04 01 00 00 movl $0x1,0x4(%esp)
- 804851f: 00
- 8048520: c7 04 24 01 00 00 00 movl $0x1,(%esp)
- 8048527: ff d0 call *%eax
- 8048529: b8 00 00 00 00 mov $0x0,%eax
- 804852e: c9 leave
- 804852f: c3 ret
复制代码 可以看出,main()分配了0x20大小的栈帧,0x1c(%esp)、0x18(%esp)用于保存局部变量f[]和t,0x14(%esp)~(%esp)用于传参,这样可以省掉出栈的过程,提高执行效率,分析的应该没错吧 ? |
|