了解局部变量所在的堆栈布局

Zuz*_* JH 3 stack gdb

我有以下代码:

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
char *MASTER_PASSWORD = "password";

bool login(char * password){
  bool is_logged_in=false;
  char buf[8];
  strcpy(buf,password);
  if(strcmp(buf, MASTER_PASSWORD)==0){
     is_logged_in=true;
  }
  return is_logged_in;
}

int main(int argc, char *argv[]){
  if(argc <2)    
  {
    printf("Syntax: %s <input string>\n", argv[0]);
    exit (0);
  }

  if(login(argv[1]))
    printf("you are authorized");

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用gdb来调试它,我需要知道is_logged_in堆栈中保存的值的位置.我怎样才能做到这一点?

sco*_*ttt 12

除非你获取局部变量(&is_logged_in)的地址,否则优化编译器通常不会将它们存储在堆栈中.你可以通过在gdb中使用info scope来看到这个:

$ gcc -Os -g3 stack-layout.c -o stack-layout
$ gdb -q stack-layout
(gdb) info scope login
Run Code Online (Sandbox Code Playgroud)

会显示:

Scope for login:
<...>
Symbol is_logged_in is multi-location:
  Range 0x40064c-0x40066e: the constant 0
  Range 0x40066e-0x400673: a complex DWARF expression:
     0: DW_OP_breg0 0 [$rax]
     2: DW_OP_const1u 32
     4: DW_OP_shl
     5: DW_OP_lit0
     6: DW_OP_eq
     7: DW_OP_stack_value

, length 1.
<...>
Run Code Online (Sandbox Code Playgroud)

即使你不熟悉x86-64程序集,也要在这里忍受.反汇编登录()给出:

8   bool login(char * password){
   0x000000000040064c <+0>:     sub    $0x18,%rsp
   0x0000000000400650 <+4>:     mov    %rdi,%rsi

9     bool is_logged_in=false;
10    char buf[8];
11    strcpy(buf,password);
   0x0000000000400653 <+7>:     lea    0x8(%rsp),%rdi
   0x0000000000400658 <+12>:    callq  0x4004c0 <strcpy@plt>

12    if(strcmp(buf, MASTER_PASSWORD)==0){
   0x000000000040065d <+17>:    mov    0x2009ec(%rip),%rsi        # 0x601050 <MASTER_PASSWORD>
   0x0000000000400664 <+24>:    lea    0x8(%rsp),%rdi
   0x0000000000400669 <+29>:    callq  0x4004f0 <strcmp@plt>
   0x000000000040066e <+34>:    test   %eax,%eax
   0x0000000000400670 <+36>:    sete   %al

13       is_logged_in=true;
14    }
15  
16    return is_logged_in;
17  }
   0x0000000000400673 <+39>:    add    $0x18,%rsp
   0x0000000000400677 <+43>:    retq
Run Code Online (Sandbox Code Playgroud)

gdb info scope关于is_logged_in的评论是什么:

  • 0x40064c0x40066e之间,即在函数的开始和对strcmp()的调用之间,is_logged_in具有常量值0.
  • 0x40066e0x400673之间,即在调用strcmp()直到函数结束之后,is_logged_in的值可以通过以下公式计算:
    • 读取存储strcmp()(RAX)返回值的64位寄存器
    • 左移32位
    • 将结果与0 进行比较.如果相等比较为真,则is_logged_in的值为1,否则为0.

在这一点上,有些人可能会争辩说,如果我们使用较低的优化级别进行编译,则is_logged_in将被不同地分配,但我的观点是,如果您获取其地址并使用该编译器执行某些操作,则只保证局部变量位于堆栈中不会优化.在这种情况下,如果要更改is_logged_in的值,最好更改strcmp()返回的值,即在strcmp()返回后立即更改RAX.

如果is_logged_in被分配在栈上,P&is_logged_in将打印其GDB地址.如果它不在堆栈上,你会收到类似的错误

(gdb) p &is_logged_in
Can't take address of "is_logged_in" which isn't an lvalue.
Run Code Online (Sandbox Code Playgroud)

dwarfstd.org记录了DWARF调试信息格式,包括其堆栈机器操作.