文章目录
  1. 1. 基础知识
    1. 1.1. stack frame
    2. 1.2. 寄存器
      1. 1.2.1. EIP
      2. 1.2.2. EBP
      3. 1.2.3. ESP
    3. 1.3. 汇编指令
      1. 1.3.1. CALL操作
      2. 1.3.2. LEAVE操作
      3. 1.3.3. RET操作
  2. 2. 栈跟踪
    1. 2.1. 代码
    2. 2.2. gdb跟踪

基础知识

stack frame

Stack frame(栈帧)是一个为函数保留的区域,用来存储关于参数、局部变量和返回地址的信息。

stack_frame

寄存器

EIP

$eip:instruction pointer
EIP寄存器存放下一条要执行的指令地址。

EBP

$ebp:frame pointer
EBP寄存器指向stack frame的基地址。

If $ebp for some stack frame is stored at addr X then $eip for that frame is stored at addr X + 4.

ESP

$esp:Stack Pointer(SP)
ESP寄存器指向栈顶。

register

汇编指令

CALL操作

把返回地址压入栈中并且跳转到调用函数开始处并且执,具体步骤:
1.Push eip : the return address
2.Push ebp : saves previous frame pointer
3.Copy sp into fp : ebp = esp
4.The new AR’s frame pointer will be the previous value of the stack pointer
5.Advance sp (esp) for allocations on stack (that is, decrement it)

LEAVE操作

1.Load ebp into esp: move %ebp, %esp
2.Restore ebp from the stack: pop %ebp

RET操作

调用RET前,程序员要保证栈指针ESP指向的位置刚好为CALL指令保存的返回地址(EIP),具体步骤:
1.Move contents of ebp into esp.
2.Increment esp by 4.
3.esp should now point to eip.
4.RET will load the value stored in esp into the eip register then jump to that value.

stack

栈跟踪

代码

gcc -g test.c -o test

  #include

void funcc(int c)
{
    printf("%d\n", c);
}

void funcb(int b, int c)
{
    printf("%d\n", b);
    funcc(c);
}

void funca(int a, int b, int c)
{
    printf("%d\n", a);
    funcb(b, c);
}

void main()
{
    int a = 4;
    int b = 3;
    int c = 2;
    int d = 1;
    funca(a, b, c);
    printf("%d\n", d);
}

gdb跟踪

1.对函数funcc设置断点,运行并查看寄存器信息,EIP为0x8048423,EBP为0xbfffefa8。

info reg

2.对地址0x8048423反汇编,在funcc的栈帧中,EIP存放下一条要执行的语句地址。

gdb eip

3.通过funcc栈帧中EBP指向基地址+4来读取返回地址(0xbfffefa8+4),读取该地址信息得到0x0804845c。
对地址0x0804845c反汇编后得到该地址指向leave操作,就是说funcb调用完funcc后要执行的下一条指令就是leave指令。

gdb ebp

4.执行完funcc后进入到funcb的栈帧,查看寄存器信息, EIP为0x804845c,EBP为0xbfffefc8。

info reg_b

5.对地址0x804845c反汇编,在funcb的栈帧中,EIP存放下一条要执行的语句地址。

gdb eip_b

6.通过funcb栈帧中EBP指向基地址+4来读取返回地址(0xbfffefc8+4),读取该地址信息得到0x0804845c。
对地址0x0804845c反汇编后得到该地址指向leave操作,就是说funca调用完funcb后要执行的下一条指令就是leave指令。

gdb ebp_b

7.执行完funcb后进入到funca的栈帧,查看寄存器信息, EIP为0x8048489,EBP为0xbfffefe8。

info reg_a

8.对地址0x8048489反汇编,在funca的栈帧中,EIP存放下一条要执行的语句地址。

gdb eip_a

9.通过funca栈帧中EBP指向基地址+4来读取返回地址(0xbfffefe8+4),读取该地址信息得到0x0804845c。
对地址0x080484d0反汇编,main调用完funca后要调用的printf。

gdb ebp_a

10.执行完funca后进入到main的栈帧,查看寄存器信息, EIP为0x80484d0,EBP为0xbffff018。

info reg_main

11.对地址0x80484d0反汇编,在main的栈帧中,EIP存放下一条要执行的语句地址。

gdb eip_main

12.通过funca栈帧中EBP指向基地址+4来读取返回地址(0xbffff018+4),读取该地址信息得到0xb7e30a83。
对地址0xb7e30a83反汇编,main返回后就会执行exit操作。

gdb ebp_main