3

次のコードがあります。

#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;
}

私はそれをデバッグするためにgdbを使用しis_logged_inています。スタックのどこに値が保存されているかを知る必要があります。どうやってやるの?

4

1 に答える 1

15

ローカル変数 ( &is_logged_in) のアドレスを取得しない限り、最適化コンパイラはほとんどの場合、それらをスタックに格納しません。これは、gdb でinfo スコープを使用して確認できます。

$ gcc -Os -g3 stack-layout.c -o stack-layout
$ gdb -q stack-layout
(gdb) info scope login

次のように表示されます。

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.
<...>

x86-64 アセンブリに慣れていなくても、ここで我慢してください。login()を逆アセンブルすると、次のようになります。

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

is_logged_ingdb info scopeについてのコメント:

  • 0x40064c0x40066eの間、つまり関数の開始とstrcmp ()の呼び出しの間では、is_logged_inの定数値は 0 です。
  • 0x40066e0x400673の間、つまり strcmp ()の呼び出し後から関数の最後まで、is_logged_inの値は次のように計算できます。
    • strcmp()からの戻り値を格納する 64 ビット レジスタの読み取り( RAX )
    • 左シフト 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.

スタック マシン操作を含む DWARF デバッグ情報形式は、dwarfstd.orgで文書化されています。

于 2013-05-24T22:58:41.937 に答える