C 声明变量堆栈

C 声明变量堆栈,c,debugging,variables,gdb,stack,C,Debugging,Variables,Gdb,Stack,我有两个文件名auth_overflow&auth_overflow2,唯一的区别是变量声明的顺序。我的问题是,根据FILO(先进先出),声明序列是否会影响它们的堆栈序列 验证溢出 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

我有两个文件名auth_overflow&auth_overflow2,唯一的区别是变量声明的顺序。我的问题是,根据FILO(先进先出),声明序列是否会影响它们的堆栈序列

验证溢出

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
交换变量后的预期输出:

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

我在第6行和第7行之间交换了地址,我希望它们相应的地址也能交换。相反,尽管进行了交换,它们的地址仍然保持不变。有什么解释吗?谢谢。

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

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

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

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

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

例如:

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

    return a + b;
}

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

    return a + b;
}
将由GCC编译为以下汇编程序代码: [gcc-S——详细的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
.text
.对齐2
.全球食品
.输入foo,%function
傅:
@功能支持互通。
@args=0,假装=0,帧=8
@帧\u needed=1,使用\u匿名\u args=0
@链接寄存器保存已消除。
str fp,[sp,#-4]!@,
添加fp、sp、#0@、,,
副警司,警司#12,,,
ldr r2,[fp,#-8]@tmp136,a
ldr r3,[fp,#-12]@tmp137,b
rsb r3,r3,r2@D.4069,tmp137,tmp136
mov r0,r3@,
添加sp、fp、#0@、,,
ldmfd sp!,{fp}
bx-lr
.尺寸为foo,.-foo
.对齐2
.全球律师协会
.类型栏,%function
酒吧:
@功能支持互通。
@args=0,假装=0,帧=8
@帧\u needed=1,使用\u匿名\u args=0
@链接寄存器保存已消除。
str fp,[sp,#-4]!@,
添加fp、sp、#0@、,,
副警司,警司#12,,,
ldr r2,[fp,#-8]@tmp136,a
ldr r3,[fp,#-12]@tmp137,b
rsb r3,r3,r2@D.4067,tmp137,tmp136
mov r0,r3@,
添加sp、fp、#0@、,,
ldmfd sp!,{fp}
bx-lr
.尺寸条,.-条

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

根据@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堆栈保护器


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

这取决于编译器和优化器选项。有些编译器会根据大小等因素对堆栈上的局部变量重新排序。您使用的是64位计算机吗?如果是这种情况,堆栈的行为与32位机器略有不同,那么变量将被推入寄存器,然后被推入64位机器上的堆栈。是的,我附加了“list”命令输出,以表明我已经重新编译了代码。交换声明后,我同意交换内存地址。但是它就是不工作。它是64位计算机吗?我只是在我的计算机上运行了溢出和溢出2。我用2个变量制作了一个非常简单的程序,然后交换它们,正如预期的那样,内存位置被交换为auth_overflow2。我使用的是64位mac电脑,因此64位或32位操作系统似乎没有问题。有关您计算机的一些信息可能会有用,因为大多数情况下这些信息取决于编译器和机器体系结构的类型。
bash-4.2$uname-a
Linux darkstar 3.10.17#2 Wed Oct 23 17:46:52 CDT 2013 i686 Intel(R)Core(TM)2个四CPU Q9550@2.83GHz GenuineIntel GNU/Linux
bash-4.2$gcc--version
gcc(gcc)4.8.2如果您所说的是正确的,那么我担心我正在读的书会把我引向错误的方向。仅仅是因为书中提到了交换变量后的内存位置交换。谢谢你澄清我的疑问。大多数答案不是我写的,而是编译器写的。;-)感谢您的澄清,我将继续寻找答案,因为结果因机器类型而异:-)“我对GCC的观察是变量按字母顺序排序。”对于相同大小的变量,这可能是正确的,但我相信大小是第一个排序维度。事实上,我有一个
char-test\u数组[16]
和一个
int-test\u-int
,并且,无论我如何声明它们,int始终位于堆栈中数组的上方(它们之间的距离始终是4个字节)。我认为,基于非常小的样本大小,您得出的结论不一定正确。编译器可以随意安排局部变量。事实上,局部变量可能根本不在堆栈上表示(使用寄存器),或者如果编译器检测到变量处于活动使用状态的时间没有重叠,则可以将同一位置用于多个局部变量。编译程序
(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