为什么调用堆栈设置如此?

Car*_*ers 5 c x86 stack

我正在玩调用堆栈,尝试更改函数的返回地址等,并在C中编写此程序:

#include<stdio.h>

void trace(int);
void func3(int);
void func2(int);
void func1(int);

int main(){

    int a = 0xAAAA1111;

    func1(0xFCFCFC01);

    return 0;

}

void func1(int a){

    int loc = 0xBBBB1111;

    func2(0xFCFCFC02);

}

void func2(int a){

    int loc1 = 0xCCCC1111;
    int loc2 = 0xCCCC2222;

    func3(0xFCFCFC03);

}

void func3(int a){

    int loc1 = 0xDDDD1111;
    int loc2 = 0xDDDD2222;
    int loc3 = 0xDDDD3333;

    trace(0xFCFCFC04);

}

void trace(int a){

    int loc = 0xEEEE1111;

    int *ptr = &loc;

    do {
    printf("0x%08X : %08X\n", ptr, *ptr, *ptr);
    } while(*(ptr++) != 0xAAAA1111);

}
Run Code Online (Sandbox Code Playgroud)

(抱歉长度)

这产生了以下输出(我添加了评论):

0xBF8144D4 : EEEE1111 //local int in trace
0xBF8144D8 : BF8144F8 //beginning of trace stack frame
0xBF8144DC : 0804845A //return address for trace to func3
0xBF8144E0 : FCFCFC04 //int passed to trace
0xBF8144E4 : 08048230 //(possibly) uninitialized padding
0xBF8144E8 : 00000000 //padding
0xBF8144EC : DDDD3333 //local int in func3
0xBF8144F0 : DDDD2222 //local int in func3
0xBF8144F4 : DDDD1111 //local int in func3
0xBF8144F8 : BF814518 //beginning of func3 stack frame
0xBF8144FC : 08048431 //return address for func3 to func2
0xBF814500 : FCFCFC03 //parameter passed to func3
0xBF814504 : 00000000 //padding
0xBF814508 : 00000000 //padding
0xBF81450C : 00000000 //padding
0xBF814510 : CCCC2222 //local int in func2
0xBF814514 : CCCC1111 //local int in func2
0xBF814518 : BF814538 //beginning of func2 stack frame
0xBF81451C : 0804840F //return address for func2 to func1
0xBF814520 : FCFCFC02 //parameter passed to func2
0xBF814524 : 00000000 //padding
0xBF814528 : BF816728 //uninitialized padding
0xBF81452C : B7DF3F4E //uninitialized padding
0xBF814530 : B7EA61D9 //uninitialized padding
0xBF814534 : BBBB1111 //local int in func1
0xBF814538 : BF814558 //beginning of func1 stack frame
0xBF81453C : 080483E8 //return address for func1 to main
0xBF814540 : FCFCFC01 //parameter passed to func1
0xBF814544 : 08049FF4 //(maybe) padding
0xBF814548 : BF814568 //(maybe) padding
0xBF81454C : 080484D9 //(maybe) padding
0xBF814550 : AAAA1111 //local int in main
Run Code Online (Sandbox Code Playgroud)

我想知道是否有人可以填补我这里的空白点......我正在运行Ubuntu linux编译与gcc 4.3.3(在x86上 - AMD Turion 64)

什么是0804 ......号码?从底部开始的第三个地址是什么?那是main的返回地址吗?如果是这样,为什么它与堆栈的其余部分相比无序? 0x0804数字是返回地址,或指向代码/数据的指针,而0xBF814数字是堆栈指针

这是什么:

0xBF814524 : 00000000 //padding?
0xBF814528 : BF816728 //I have no idea
0xBF81452C : B7DF3F4E //????
0xBF814530 : B7EA61D9 //????
Run Code Online (Sandbox Code Playgroud)

刚刚在func1中看到本地int之后?

好吧,我的堆栈转储几乎完全填满了.

看起来编译器希望将参数压入堆栈,从0x ....... 0地址开始,并且函数之前的局部变量和被调用函数的第一个参数之间的所有内容似乎都是填充(无论是0x00000000还是一些未初始化的值).其中一些我不确定,因为它们看起来像代码/数据段指针,但我看不到它们在代码中使用:当堆栈指针减少时它们就在那里.

知道在任何类型的项目中触摸调用堆栈都是一个巨大的nono,但这没关系.这很有趣,对吧?

格雷格希望看到集会,就在这里

    .file   "stack.c"
    .text
.globl main
    .type   main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp
    movl    $-1431695087, -8(%ebp)
    movl    $-50529279, (%esp)
    call    func1
    movl    $0, %eax
    addl    $20, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
.globl func1
    .type   func1, @function
func1:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-1145368303, -4(%ebp)
    movl    $-50529278, (%esp)
    call    func2
    leave
    ret
    .size   func1, .-func1
.globl func2
    .type   func2, @function
func2:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-859041519, -4(%ebp)
    movl    $-859037150, -8(%ebp)
    movl    $-50529277, (%esp)
    call    func3
    leave
    ret
    .size   func2, .-func2
.globl func3
    .type   func3, @function
func3:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-572714735, -4(%ebp)
    movl    $-572710366, -8(%ebp)
    movl    $-572705997, -12(%ebp)
    movl    $-50529276, (%esp)
    call    trace
    leave
    ret
    .size   func3, .-func3
    .section    .rodata
.LC0:
    .string "0x%08X : %08X\n"
    .text
.globl trace
    .type   trace, @function
trace:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    $-286387951, -4(%ebp)
    leal    -4(%ebp), %eax
    movl    %eax, -8(%ebp)
.L10:
    movl    -8(%ebp), %eax
    movl    (%eax), %edx
    movl    -8(%ebp), %eax
    movl    (%eax), %eax
    movl    %edx, 12(%esp)
    movl    %eax, 8(%esp)
    movl    -8(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    -8(%ebp), %eax
    movl    (%eax), %eax
    cmpl    $-1431695087, %eax
    setne   %al
    addl    $4, -8(%ebp)
    testb   %al, %al
    jne .L10
    leave
    ret
    .size   trace, .-trace
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

Gre*_*ill 5

像这样检查堆栈似乎是一步太远了.我建议您在调试器中加载程序,切换到汇编语言视图,然后单步执行每个机器指令.了解CPU堆栈必然需要了解在其上运行的机器指令,这将是一种更直接的方式来查看正在发生的事情.

正如其他人所提到的,堆栈的结构也高度依赖于您正在使用的处理器架构.


rpe*_*ich 5

最有可能的是堆栈金丝雀.您的编译器添加了代码以将其他数据推送到堆栈并随后将其读回以检测堆栈溢出.


Nic*_*yer 5

我猜这些以0x0804开头的值是程序代码段中的地址(如函数调用的返回地址).以0xBF814开头的标记为返回地址的是堆栈上的地址 - 数据,而不是代码.我猜它们可能是帧指针.


laa*_*lto 3

正如已经指出的,0xBF... 是帧指针,0x08... 返回地址。

填充是由于对齐问题造成的。其他无法识别的值也会被填充,因为堆栈未初始化为零或任何其他值。未初始化的变量和未使用的填充空间将包含这些内存位置中的任何字节。