免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5043 | 回复: 7
打印 上一主题 下一主题

模拟GDB输出Call Stack [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-09-08 14:44 |只看该作者 |倒序浏览
利用空闲写了一个模拟GDB输出Call Stack的程序。

#include <stdio.h>

#define _bt() \
        ({ unsigned int ret;\
        __asm__("mov 0x4(%%ebp), %0;"\
                :"=r"(ret));\
        printf("%s(%x)\n", __FUNCTION__, ret);\
        ret;})

#define get_cur_ebp() \
        ({ unsigned int ebp;\
        __asm__("mov %%ebp, %0;"\
                :"=r"(ebp));\
        ebp;})

#define get_ret() \
        ({ unsigned int ret;\
        __asm__("mov 0x4(%%ebp), %0;"\
                :"=r"(ret));\
        ret;})

#define pop_frame() \
        {__asm__("mov (%%ebp), %%eax;\
                mov %%eax, %%ebp;"
:::"%eax");}

#define restore_ebp(ebp) \
        {__asm__("mov %0, %%eax;\
                mov %%eax, %%ebp;"
\
                ::"r"(ebp):"%eax");}


int main(int, char**);

unsigned int org_ebp;

unsigned int ret;

void print_stack_trace()
{
        org_ebp = get_cur_ebp();

        printf("%x\n", get_ret());

        pop_frame();
        while (1)
        {
                ret = get_ret();
                printf("%x\n", ret);
                if ( ret > (unsigned int)main )
                        break;
                else
                        pop_frame();
        }

        restore_ebp(org_ebp);
}

int foo(int arg)
{
        print_stack_trace();
        printf("left foo(%d)\n", arg);
        return arg;
}

void bar()
{
        foo(1);
        print_stack_trace();
        printf("left bar\n");
}

int main(int argc, char* argv[])
{
        bar();
        foo(0);
        print_stack_trace();
        return 0;
}


gcc -Wall -g编译,部分objdump输出
080483fa <foo>:
80483fa:       55                      push   %ebp
80483fb:       89 e5                   mov    %esp,%ebp
80483fd:       83 ec 08                sub    $0x8,%esp
8048400:       e8 7f ff ff ff          call   8048384 <print_stack_trace>
8048405:       8b 45 08                mov    0x8(%ebp),%eax
8048408:       89 44 24 04             mov    %eax,0x4(%esp)
804840c:       c7 04 24 bc 85 04 08    movl   $0x80485bc,(%esp)
8048413:       e8 a0 fe ff ff          call   80482b8 <printf@plt>
8048418:       8b 45 08                mov    0x8(%ebp),%eax
804841b:       c9                      leave  
804841c:       c3                      ret   

0804841d <bar>:
804841d:       55                      push   %ebp
804841e:       89 e5                   mov    %esp,%ebp
8048420:       83 ec 08                sub    $0x8,%esp
8048423:       c7 04 24 01 00 00 00    movl   $0x1,(%esp)
804842a:       e8 cb ff ff ff          call   80483fa <foo>
804842f:       e8 50 ff ff ff          call   8048384 <print_stack_trace>
8048434:       c7 04 24 ca 85 04 08    movl   $0x80485ca,(%esp)
804843b:       e8 78 fe ff ff          call   80482b8 <printf@plt>
8048440:       c9                      leave  
8048441:       c3                      ret   

08048442 <main>:
8048442:       55                      push   %ebp
8048443:       89 e5                   mov    %esp,%ebp
8048445:       83 ec 08                sub    $0x8,%esp
8048448:       83 e4 f0                and    $0xfffffff0,%esp
804844b:       b8 00 00 00 00          mov    $0x0,%eax
8048450:       83 c0 0f                add    $0xf,%eax
8048453:       83 c0 0f                add    $0xf,%eax
8048456:       c1 e8 04                shr    $0x4,%eax
8048459:       c1 e0 04                shl    $0x4,%eax
804845c:       29 c4                   sub    %eax,%esp
804845e:       e8 ba ff ff ff          call   804841d <bar>
8048463:       c7 04 24 00 00 00 00    movl   $0x0,(%esp)
804846a:       e8 8b ff ff ff          call   80483fa <foo>
804846f:       e8 10 ff ff ff          call   8048384 <print_stack_trace>
8048474:       b8 00 00 00 00          mov    $0x0,%eax
8048479:       c9                      leave  
804847a:       c3                      ret



程序的输出:
root:~/asm$ gcc -Wall -g -o bt bt.c
root:~/asm$ ./bt
8048405      <-- foo
804842f      <-- bar
8048463      <-- main
left foo(1)
8048434      <-- bar
8048463      <-- main
left bar
8048405      <-- foo
804846f      <-- main
left foo(0)
8048474      <-- main
b7e39e00     <-- code that call main

<-- xxx 非程序输出。

GDB在断点处会打印EIP值,我这个没办法,内嵌的函数,选择同样打印返回地址。要完全模拟可以向上跳过call print_stack_trace(该死的变长指令)。

代码只是雏形,得保证foo、bar这样的函数体在main函数体之前。涉及到多个文件,需要这样 gcc -o xxx a.o b.o main.o
如果再进一步解析生成的ELF,就能像GDB一样打印 0xaabbccdd in foo()。当然-g参数很无敌,参数都能打印出来。

论坛徽章:
0
2 [报告]
发表于 2008-09-08 14:53 |只看该作者
VC6版的,原以为可以顺利移植,只要把AT&T翻译成Intel。。。

#include <stdio.h>

#define DWORD unsigned int
#define NAKED __declspec(naked)

DWORD NAKED get_cur_ebp()
{
   __asm
   {
      mov eax, ebp
      ret
   }
}

DWORD NAKED get_ret()
{
   __asm
   {
      mov eax, [ebp + 4]
      ret
   }
}

void NAKED pop_frame()
{
   __asm
   {
      mov eax, [ebp]
      mov ebp, eax
      ret
   }
}

void NAKED restore_ebp(DWORD _ebp)
{
   __asm
   {
      mov ebp, [esp + 4]
      ret
   }
}

int main(int, char**);

DWORD org_ebp;
DWORD ret;
DWORD main_base = (DWORD)main + 5 + *(DWORD*)((char*)main + 1);
//DWORD main_base = (DWORD)main;


void print_stack_trace()
{
   org_ebp = get_cur_ebp();

   printf("%x\n", get_ret());

   pop_frame();
   while (1)
   {
      ret = get_ret();
      printf("%x\n", ret);

      if ( ret > main_base )
         break;
      else
         pop_frame();
   }

   restore_ebp(org_ebp);
}

int foo(int arg)
{
   print_stack_trace();
   printf("left foo(%d)\n", arg);
   return arg;
}

void bar()
{
   foo(1);
   print_stack_trace();
   printf("left bar()\n");
   return;
}

int main(int argc, char* argv[])
{
   bar();
   foo(0);
   printf("%d\n", foo(2));
   print_stack_trace();
   return 0;
}



VC6对宏支持的不好(或者是我写的不标准?),而且默认还开启增量链接,代码中的main_base就是为了对付增量链接。如果关闭增量链接,用注释掉的main_base。

论坛徽章:
0
3 [报告]
发表于 2008-09-08 14:54 |只看该作者
VC6版的,原以为可以顺利移植,只要把AT&T翻译成Intel。。。

#include <stdio.h>

#define DWORD unsigned int
#define NAKED __declspec(naked)

DWORD NAKED get_cur_ebp()
{
   __asm
   {
      mov eax, ebp
      ret
   }
}

DWORD NAKED get_ret()
{
   __asm
   {
      mov eax, [ebp + 4]
      ret
   }
}

void NAKED pop_frame()
{
   __asm
   {
      mov eax, [ebp]
      mov ebp, eax
      ret
   }
}

void NAKED restore_ebp(DWORD _ebp)
{
   __asm
   {
      mov ebp, [esp + 4]
      ret
   }
}

int main(int, char**);

DWORD org_ebp;
DWORD ret;
DWORD main_base = (DWORD)main + 5 + *(DWORD*)((char*)main + 1);
//DWORD main_base = (DWORD)main;


void print_stack_trace()
{
   org_ebp = get_cur_ebp();

   printf("%x\n", get_ret());

   pop_frame();
   while (1)
   {
      ret = get_ret();
      printf("%x\n", ret);

      if ( ret > main_base )
         break;
      else
         pop_frame();
   }

   restore_ebp(org_ebp);
}

int foo(int arg)
{
   print_stack_trace();
   printf("left foo(%d)\n", arg);
   return arg;
}

void bar()
{
   foo(1);
   print_stack_trace();
   printf("left bar()\n");
   return;
}

int main(int argc, char* argv[])
{
   bar();
   foo(0);
   printf("%d\n", foo(2));
   print_stack_trace();
   return 0;
}



VC6对宏支持的不好(或者是我写的不标准?),而且默认还开启增量链接,代码中的main_base就是为了对付增量链接。如果关闭增量链接,用注释掉的main_base。

论坛徽章:
0
4 [报告]
发表于 2008-09-08 14:55 |只看该作者
抱歉,在Websense后面,发多了。。。

论坛徽章:
0
5 [报告]
发表于 2008-09-08 19:20 |只看该作者
glibc有支持?估计还是利用stack frame吧,按说这东西跟C语言没什么关系啊

论坛徽章:
0
6 [报告]
发表于 2008-09-09 08:52 |只看该作者
我用c++ 做过一个,利用全局变量 和 栈变量。 不是很好

论坛徽章:
0
7 [报告]
发表于 2008-09-09 13:12 |只看该作者
利用栈变量?莫非在所有函数进入时在栈里记录,退出前弹出去?也行,就是要改的地方有点多:wink:

不知道我理解的对不

论坛徽章:
0
8 [报告]
发表于 2008-09-09 13:54 |只看该作者
不错啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP