2011-05-28 105 views
1

测验:如何通过查看堆栈值来生成回溯?如何通过查看堆栈值来生成回溯?

0xf3e2de34 f3e2de70 c0135351 401ef021 00000000 p.bsQS...p...... 
0xf3e2de44 f3e2de81 00000021 f3e2c000 f7950924 ..bs......bs...w 
0xf3e2de54 00000000 401ef000 00000246 00000246 .....p..F...F... 
0xf3e2de64 00000001 00000001 f7950000 f3e2df18 ...........w..bs 
0xf3e2de74 c02898b5 322d7875 23203235 00007820 5...ux.252...x.. 
0xf3e2de84 f3e2de98 00000000 f7950bd0 bffff0ec ..bs....P..wlp.. 
0xf3e2de94 c70cf660 00000000 00000000 bffff0ec .v.G........lp.. 
0xf3e2dea4 f3e2c000 f7950930 7fffffff 00000000 ..bs0..w........ 
0xf3e2deb4 00000000 00000001 00000000 bffff07b .............p.. 
0xf3e2dec4 f5cfd880 bffff07b 00000000 f5f24740 .XOu.p.......Gru 
0xf3e2ded4 c0124f87 00000000 00000000 00200200 .O.............. 
0xf3e2dee4 f3e2defc 067b3067 00000000 f5f24740 ..bsg0.......Gru 
0xf3e2def4 c0124f87 f7950934 f7950934 c028331b .O..4..w4..w.3.. 
0xf3e2df04 00000000 c01b0fe8 f5cfd880 f7950000 ....h....XOu...w 
0xf3e2df14 fffffffb f3e2df50 c0283642 00000001 ....P.bsB6...... 
0xf3e2df24 00000001 00000001 f3e2c000 f6fe30d0 ..........bsP0.v 

提示 - 当前函数总是可以从EIP

确定我发现了这个问题kernelnewbies/ABI文档。
我真的不明白那里给出的提示吗?(也许是因为我对此不知道)。

有人能解释我如何解决这些有点问题。

回答

4

在这种情况下,您可以非常可靠地做到这一点,因为该代码已经在启用帧指针的情况下编译。

你需要知道什么:

  • EIP是指向下一个指令执行的寄存器。
  • 当调用一个函数时,参数和EIP(所以被调用函数知道返回的位置)保存在堆栈中。当编译器被告知(显式或隐式地)使用帧指针时,它然后将栈指针(在EBP寄存器中)保存在堆栈上(以便它可以稍后将帧指针恢复到它所具有的值在调用函数上),然后将帧指针设置为指向堆栈的当前顶部。这允许从已知参考点(帧指针)轻松访问参数和局部变量,并且极大地简化了调试。

  • 然后,为局部变量保留空间,并执行该功能。
  • 从函数返回时,前一帧指针和指令指针被恢复。

因此,要生成回溯,您需要遵循帧指针,查看相应的保存的EIPS。所以:

current function was called from c0135351 
follow f3e2de70 → was called from c02898b5 
follow f3e2df18 → was called from c0283642 

当然,这是简单的情况。当你没有帧指针时,你必须猜测栈上给定的值是否与指令指针相对应。

缺少的部分是如何将这些数字转换为函数名。有一个未删除的vmlinux(注意x,而不是z)文件是非常宝贵的。 System.map只包含一些符号,所以很多时候你只会知道相关函数在函数A和函数B之间。

编辑:

x86上的函数调用看起来是这样的:

         ... 
int main()        add $-0x8,%esp ; alignment 
{          push $0x2  ; arg 2 
     ...        push $0x1  ; arg 1 
     func(1, 2);      call func  ; function call 
     ...        add $0x10,%esp ; pop args from stack 
}          ... 

和被调用函数看起来是这样的:

void func(int arg1, int arg2)   push %ebp  ;\ 
{          mov %esp,%ebp ;/ create stack frame 
     int local1;      sub $0x18,%esp ; reserves space 
     ...        ... 
}          mov %ebp,%esp ;\ 
             pop %ebp  ;/ destroys frame 
             ret    ; returns 

因此,堆栈将类似于到:

  :   : 
      +-----------+ 
      : alignment : 
      +-----------+ 
12(%ebp) | arg2 | 
      +-----------+ 
8(%ebp) | arg1 | 
      +-----------+ 
4(%ebp) | ret | -----> return address 
      +-----------+ 
    (%ebp) | ebp | -----> previous ebp 
      +-----------+ 
-4(%ebp) | local1 | -----> local vars 
      +-----------+ 
      : alignment : 
      +-----------+ 
      :   : 

(下地址在ASCII艺术更低)

所以,如果你把下面的保存EBP指针,你可以得到保存EIP指针(ret以上),这点在调用链指令(在回报链中,准确而言)。

+0

嗨Ninjalj,+1谢谢你的回答,但坦率的说我明白了很多这个描述。你能不能来更低一级,给我一个关于如何理解和解决问题的图片。 – Sen 2011-05-30 07:30:20

0

EIP是我认为的指令指针 - 它保存了当前正在执行的指令的地址。