声明变量堆栈

时间:2014-01-17 05:39:16

标签: c debugging variables gdb stack

我有2个文件名称auth_overflow& auth_overflow2,唯一的区别是变量声明的序列。我的问题是,声明序列是否会根据FILO影响其堆栈序列(先后出)?

auth_overflow

bash-4.2$ gdb -q auth_overflow
Reading symbols from /home/reader/hacking/auth_overflow...done.
(gdb) list
5       int check_authetication (char *password) {
6               int auth_flag = 0;
7               char password_buffer[16];
8
9               strcpy(password_buffer, password);

(gdb) break 9
Breakpoint 1 at 0x804850d: file auth_overflow.c, line 9.
(gdb) run AAAAAAAAAAAA
Starting program: /home/reader/hacking/auth_overflow AAAAAAAAAAAA

Breakpoint 1, check_authetication (password=0xbffff7f3 'A' <repeats 12 times>) at auth_overflow.c:9
9               strcpy(password_buffer, password);
(gdb) x/x password_buffer
0xbffff52c:     0x08048330
(gdb) x/x &auth_flag
0xbffff53c:     0x00000000

auth_overflow2

bash-4.2$ gdb -q auth_overflow2
Reading symbols from /home/reader/hacking/auth_overflow2...done.
(gdb) list
5       int check_authetication (char *password) {
6               char password_buffer[16];
7               int auth_flag = 0;
8
9               strcpy(password_buffer, password);

(gdb) break 9
Breakpoint 1 at 0x804850d: file auth_overflow2.c, line 9.
(gdb) run AAAAAAAAAAAA
Starting program: /home/reader/hacking/auth_overflow2 AAAAAAAAAAAA

Breakpoint 1, check_authetication (password=0xbffff7f2 'A' <repeats 12 times>) at auth_overflow2.c:9
9               strcpy(password_buffer, password);
(gdb) x/x password_buffer
0xbffff52c:     0x08048330
(gdb) x/x &auth_flag
0xbffff53c:     0x00000000

正常输出:

(gdb) x/x password_buffer
0xbffff52c:     0x08048330
(gdb) x/x &auth_flag
0xbffff53c:     0x00000000

变量交换后的预期输出:

(gdb) x/x password_buffer
0xbffff53c:     0x08048330
(gdb) x/x &auth_flag
0xbffff52c:     0x00000000

我换了第6行和第6行。 7我希望他们的相应地址也可以交换。相反,尽管交换,他们的地址保持不变。这有什么解释吗?谢谢。

3 个答案:

答案 0 :(得分:1)

变量声明的顺序无关紧要,因为它只是一个声明。

当您将变量定义为局部变量(在堆栈上)时,编译器可以在堆栈上分配任何适当的位置,对齐变量并“重新排序”它们。它不是真正的重新排序,因为只有编译器才会选择顺序。

示例:

int foo(void)
{
    int a;
    int b;

    return a + b;
}

int bar(void)
{
    int b;
    int a;

    return a + b;
}

将由GCC编译为此汇编代码: [gcc -S --verbose-asm foo.c]

        .text
        .align  2
        .global foo
        .type   foo, %function
foo:
        @ Function supports interworking.
        @ args = 0, pretend = 0, frame = 8
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        str     fp, [sp, #-4]!  @,
        add     fp, sp, #0      @,,
        sub     sp, sp, #12     @,,
        ldr     r2, [fp, #-8]   @ tmp136, a
        ldr     r3, [fp, #-12]  @ tmp137, b
        rsb     r3, r3, r2      @ D.4069, tmp137, tmp136
        mov     r0, r3  @, <retval>
        add     sp, fp, #0      @,,
        ldmfd   sp!, {fp}
        bx      lr
        .size   foo, .-foo
        .align  2
        .global bar
        .type   bar, %function
bar:
        @ Function supports interworking.
        @ args = 0, pretend = 0, frame = 8
        @ frame_needed = 1, uses_anonymous_args = 0
        @ link register save eliminated.
        str     fp, [sp, #-4]!  @,
        add     fp, sp, #0      @,,
        sub     sp, sp, #12     @,,
        ldr     r2, [fp, #-8]   @ tmp136, a
        ldr     r3, [fp, #-12]  @ tmp137, b
        rsb     r3, r3, r2      @ D.4067, tmp137, tmp136
        mov     r0, r3  @, <retval>
        add     sp, fp, #0      @,,
        ldmfd   sp!, {fp}
        bx      lr
        .size   bar, .-bar

如您所见,变量a始终位于同一地址[fp-8]。我对GCC的观察是变量按字母顺序排序。

答案 1 :(得分:1)

根据@harper的汇编输出,编译器可以自由地重新排序变量堆栈,因此在这种情况下,它总是在int变量之前的char数组。这使得程序容易受到基于堆栈的缓冲区溢出的影响。

为了更改以下内容:

(gdb) x/x password_buffer
0xbffff52c:     0x08048330
(gdb) x/x &auth_flag
0xbffff53c:     0x00000000

进入预期产出如下:

(gdb) x/x password_buffer
0xbffff53c:     0x08048330
(gdb) x/x &auth_flag
0xbffff52c:     0x00000000

我们只需在编译期间添加-fstack-protector-all参数,结果将如预期一样。反之亦然,也许您可​​以使用-O0-fno-stack-protector

感谢@harper和@tesseract的贡献: - )

答案 2 :(得分:0)

在32位机器上,声明的顺序会影响它放在内存中的位置,不确定64位机器是如何处理的,但是从内存中它将变量推入寄存器然后再到堆栈。

假设您使用32位机器,在这种情况下必须交换内存位置,您确定在交换后编译代码吗?。

auth_overflow似乎正确, auth_overflow2应该在你的问题中给出预期的输出,不知道为什么会这样。我能想到的唯一原因是尝试重新编译代码。