C 为什么调用堆栈是这样设置的?

C 为什么调用堆栈是这样设置的?,c,x86,stack,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 = 0xBBBB

我只是在玩调用堆栈,试图更改函数的返回地址等,最后用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);

}
我想知道有没有人能告诉我这里的空白点。我使用GCC4.3.3(在x86-AMD Turion 64上)运行UbuntuLinux编译

什么是0804。。。数字?从底部往上的第三个地址是什么?那是main的回信地址吗?如果是这样,为什么与堆栈的其余部分相比它会出现故障? 0x0804数字是返回地址或指向代码/数据或其他内容的指针,而0xBF814数字是堆栈指针

这是什么

0xBF814524 : 00000000 //padding?
0xBF814528 : BF816728 //I have no idea
0xBF81452C : B7DF3F4E //????
0xBF814530 : B7EA61D9 //????
在func1中的局部int后面看到了吗

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

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

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

也 格雷格想看看集会, 给你

    .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

这是调试版本还是发布版本?我希望在调试版本中添加一些内容来检测堆栈溢出。

像这样检查堆栈似乎太遥远了。我可能建议在调试器中加载程序,切换到汇编语言视图,并单步执行每一条机器指令。理解CPU堆栈必然需要理解在其上运行的机器指令,这将是了解发生了什么的更直接的方法


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

最有可能是这样的。编译器添加代码以将附加数据推送到堆栈中,然后将其读回以检测堆栈溢出。

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

0xBF。。。地址将是指向上一堆栈帧的链接:

0xBF8144D8 : BF8144F8 //return address for trace
0xBF8144DC : 0804845A //

0xBF8144F8 : BF814518 //return address for func3
0xBF8144FC : 08048431 //????

0xBF814518 : BF814538 //return address for func2?
0xBF81451C : 0804840F //????

0xBF814538 : BF814558 //return address for func1
0xBF81453C : 080483E8 //????
0x08。。。地址将是在每种情况下返回的代码的地址


我不能为堆栈上的其他东西说话;您必须逐步了解汇编语言,并确切地了解它在做什么。我猜它是将每一帧的开始对齐到一个特定的对齐方式,这样
\uuuu attribute\uuuuu((align))
(或者现在叫什么…)就可以工作。

编译器使用EBP来存储帧的基址。已经有一段时间了,所以我看了这个,所以我可能对细节有点误解,但想法是这样的

调用函数时有三个步骤:

  • 调用方将函数的参数推送到堆栈上。
    • 调用者使用
      call
      指令,将返回地址推送到堆栈上,并跳转到新函数
    • 被调用的函数将EBP推送到堆栈上,并将ESP复制到EBP中:
    • (注意:性能良好的功能还将使用
      PUSHAD
      将所有GPRs推送到堆栈上)
  • 当函数返回它时:

  • 持久性有机污染物
  • 执行
    ret
    指令,弹出返回地址并跳到那里
  • 问题是,为什么EBP会被推,为什么ESP会被复制到EBP中

    输入函数时,ESP指向该函数堆栈上的最低点。您在堆栈上声明的任何变量都可以作为
    [ESP+offset\u to\u variable]
    访问。这很容易!但请注意,ESP必须始终指向堆栈的顶部,因此当您在堆栈上声明新变量时,ESP会发生更改。现在
    [ESP+offset\u to\u variable]
    不是很好,因为您必须记住在分配变量时ESP是什么


    函数需要做的第一件事不是这样做,而是将ESP复制到EBP中。EBP在函数的生命周期内不会改变,因此您可以使用“[EBP+offset\u to\u variable]访问所有变量。但是现在您有另一个问题,因为如果被调用的函数调用另一个函数,EBP将被覆盖。这就是为什么在复制EBP之前,需要将其保存到堆栈中,以便在返回调用函数之前将其还原。

    如前所述,0xBF。。。是帧指针和0x08。。。返回地址


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

    将代码编译成汇编并查看输出,gcc-s myfile.c-o myfile.son x86,更新了我的问题。我看了一下程序集,但它是这样做的:movl$-859041519,-4(%ebp),用十进制数字代替易于识别的十六进制,我的意思是,否则很难遵循堆栈,堆栈上可能会有一些东西。异常记录地址、临时值、cookies以检查堆栈溢出。您必须查看程序集才能了解这些值的用途。我不认为这是异常记录地址,我没有使用任何临时值,程序集似乎也没有使用金丝雀。。。我认为大多数奇怪的东西都是未初始化的填充,因为我在程序集中找不到任何对它的引用,所以它肯定正好在内存中
    0xBF8144D8 : BF8144F8 //return address for trace
    0xBF8144DC : 0804845A //
    
    0xBF8144F8 : BF814518 //return address for func3
    0xBF8144FC : 08048431 //????
    
    0xBF814518 : BF814538 //return address for func2?
    0xBF81451C : 0804840F //????
    
    0xBF814538 : BF814558 //return address for func1
    0xBF81453C : 080483E8 //????
    
    push EBP
    mov EBP, ESP
    
    pop EBP
    ret