堆栈上的变量排列 - 乱序?

逆向工程 C++ 数据库 海湾合作委员会
2021-06-25 02:44:27

作为我们大学课程的一部分,我正在练习一些逆向工程破解技巧,我有一个关于堆栈上变量排列的问题。

我有一个非常基本的 C++ 代码,如下所示:

void foo(int x){
    int a=0;
    int b=x;
    int c=3;
    int z=a+b+c;
    return;
}

int main(){
     foo(5);
     return 0;
}

我编译程序,像这样:

g++ program.cpp -o program -ggdb 

我用 gdb 运行

gdb -q program

在 gdb 里面

set disassembly-flavor intel
disass foo

我期望的堆栈帧布局foo为具有可变a的顶部b,随后cz但情况似乎并非如此。

我偶然发现了这个答案,其中的解释显示了变量排列方式的类似布局。我希望它x位于框架中的最高地址,就在ebp. 但答案清楚地表明z最接近ebp,因为它在ebp - 4xebp - 12,这对我来说似乎违反直觉。

我读了很多书,也看过一些关于缓冲区溢出的在线视频,其中说编译器遇到的第一个变量是第一个放在堆栈中的;并且变量声明的顺序可以影响哪些变量被覆盖,以防它们旁边有一个缓冲区溢出。这仍然是一个有效的声明吗?

为什么我看到变量按这样的顺序排列?请帮助我,我不确定我的事实是否已经过时,或者我是否遗漏了一些基本的东西。

PS:我使用的是 Ubuntu 16.04 桌面,最新版本的 GCC/G++

编辑1:添加功能的反汇编 foo

ubuntu@Ubuntu:~$ gdb -q program 
Reading symbols from program... 
(gdb) set disassembly intel 
(gdb) disass foo 
Dump of assembler code for function foo(int): 
    0x0000000000001125 <+0>:    push rbp    
    0x0000000000001126 <+1>:    mov rbp,rsp 
    0x0000000000001129 <+4>:    mov DWORD PTR [rbp-0x14],edi 
    0x000000000000112c <+7>:    mov DWORD PTR [rbp-0x10],0x0 
    0x0000000000001133 <+14>:   mov eax,DWORD PTR [rbp-0x14]
    0x0000000000001136 <+17>:   mov DWORD PTR [rbp-0xc],eax
    0x0000000000001139 <+20>:   mov DWORD PTR [rbp-0x8],0x3
    0x0000000000001140 <+27>:   mov edx,DWORD PTR [rbp-0x10]
    0x0000000000001143 <+30>:   mov eax,DWORD PTR [rbp-0xc]
    0x0000000000001146 <+33>:   add edx,eax 
    0x0000000000001148 <+35>:   mov eax,DWORD PTR [rbp-0x8]
    0x000000000000114b <+38>:   add eax,edx 
    0x000000000000114d <+40>:   mov DWORD PTR [rbp-0x4],eax  
    0x0000000000001150 <+43>:   nop 
    0x0000000000001151 <+44>:   pop rbp 
    0x0000000000001152 <+45>:   ret 
End of assembler dump. 
(gdb) b *foo+43 
Breakpoint 1 at 0x1150: file program.cpp, line 6. 
(gdb) r Starting program: /home/ubuntu/program Breakpoint 1, foo (x=5) at program.cpp:6 
6    return; 
(gdb) p &a 
$1 = (int *) 0x7fffffffdfb0 
(gdb) p $rbp-0x10 
$2 = (void *) 0x7fffffffdfb0 

(gdb) p &b 
$3 = (int *) 0x7fffffffdfb4 
(gdb) p $rbp-0xc 
$4 = (void *) 0x7fffffffdfb4 

(gdb) p &c 
$5 = (int *) 0x7fffffffdfb8 
(gdb) p $rbp-0x8 
$6 = (void *) 0x7fffffffdfb8 

(gdb) p &z 
$7 = (int *) 0x7fffffffdfbc 
(gdb) p $rbp-0x4 
$8 = (void *) 0x7fffffffdfbc 
(gdb)
1个回答

请记住,使用 -O 编译时的程序将变为

mov eax,0
return

在此处输入图片说明

你的源代码中的每一件事都是死代码,将被淘汰

因为您是在调试模式下编译它而没有任何优化,所以编译器会为您提供所有这些变量

我不确定你所说的更接近 ebp 是什么意思

在调试模式下,编译器尽可能按照遇到变量的顺序分配变量

这是相同代码的颜色编码未优化输出

如您所见,白色的编译器第一条指令是您的 int a=0; 这是在 rbp-4

在此处输入图片说明

但是 std iirc 没有指定任何分配变量地址的标准

引用该线程中链接的标准

每个参数都有自动存储时长;它的标识符是一个左值。164) 参数存储的布局是未指定的。