为什么这段代码可以得到环境变量地址?

时间:2016-11-08 14:14:15

标签: c linux getenv

64-bit Linux stack smashing tutorial: Part 1使用Get environment variable address gist获取环境变量地址。先决条件是首先通过echo 0 > proc/sys/kernel/randomize_va_space禁用ASLR。

要点的内容是:

/*
 * I'm not the author of this code, and I'm not sure who is.
 * There are several variants floating around on the Internet, 
 * but this is the one I use. 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char *ptr;

    if(argc < 3) {
        printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
        exit(0);
    }
    ptr = getenv(argv[1]); /* get env var location */
    ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
    printf("%s will be at %p\n", argv[1], ptr);
}

为什么*2用于调整程序名称?

我的猜测是程序名称在堆栈上方保存两次。

enter image description here

https://lwn.net/Articles/631631/中的以下图表提供了更多详细信息:

------------------------------------------------------------- 0x7fff6c845000
 0x7fff6c844ff8: 0x0000000000000000
        _  4fec: './stackdump\0'                      <------+
  env  /   4fe2: 'ENVVAR2=2\0'                               |    <----+
       \_  4fd8: 'ENVVAR1=1\0'                               |   <---+ |
       /   4fd4: 'two\0'                                     |       | |     <----+
 args |    4fd0: 'one\0'                                     |       | |    <---+ |
       \_  4fcb: 'zero\0'                                    |       | |   <--+ | |
           3020: random gap padded to 16B boundary           |       | |      | | |

在此图中,./stackdump用于执行程序。所以我可以看到程序名./stackdump一旦保存在环境字符串之上。如果从Bash shell启动./stackdump,Bashell会将其保存在包含密钥_的环境字符串中:

  

_

     

(下划线。)在shell启动时,设置为用于调用正在执行的shell或shell脚本的绝对路径名。   环境或参数列表。随后,扩展到最后   扩展后的上一个命令的参数。也设置为   用于调用执行并放入的每个命令的完整路径名   环境导出到该命令。检查邮件时,这个   参数保存邮件文件的名称。

环境字符串位于堆栈之上。因此程序名称会在堆栈上方另一次保存。

2 个答案:

答案 0 :(得分:1)

如果有人还在想为什么。这是因为除了在所有环境变量之前推送到堆栈之外,程序名称也存储在环境变量名称“_”中。

您可以通过将gdb附加到进程来检查这一点,并检查最后一个环境变量下面的堆栈内容。假设0x7fffffffabcd是最后一个环境变量的地址:

$ gdb -p <pid>

(gdb) x/20s 0x7fffffffabcd

存储在argv[0]中的程序名称不会影响环境变量的地址,因为它位于堆栈上最后一个环境变量的顶部。

答案 1 :(得分:0)

将以下代码保存为stackdump.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/auxv.h>

int main(int argc, char *argv[]) {
  char *ptr;
  int i;

  for (i = 0; i < argc; i++) {
    printf("  argv[%d]: %p, %p, %s\n", i, argv + i, argv[i], argv[i]);
  }

  char * program = (char *)getauxval(AT_EXECFN);
  printf("AT_EXECFN:               , %p, %s\n", program, program);
  char* path = getenv("PATH");
  printf("     PATH:               , %p, %s\n", path, path);
  char* underscore = getenv("_");
  printf("        _:               , %p, %s\n", underscore, underscore);
}

首先,运行gcc -o stackdump stackdump.c来编译代码。其次,执行echo 0 > proc/sys/kernel/randomize_va_space。第三,运行./stackdump zero one two给予:

  argv[0]: 0x7fffffffe4a8, 0x7fffffffe6e5, ./stackdump
  argv[1]: 0x7fffffffe4b0, 0x7fffffffe6f1, zero
  argv[2]: 0x7fffffffe4b8, 0x7fffffffe6f6, one
  argv[3]: 0x7fffffffe4c0, 0x7fffffffe6fa, two
AT_EXECFN:               , 0x7fffffffefec, ./stackdump
     PATH:               , 0x7fffffffee89, /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/cloud-user/.local/bin:/home/cloud-user/bin
        _:               , 0x7fffffffefe0, ./stackdump

./stackdump的三个副本位于程序的地址空间中,如上所示。其中两个地址的地址高于PATH,如下所示:

AT_EXECFN: 0x7fffffffefec, ./stackdump
        _: 0x7fffffffefe0, ./stackdump
     PATH: 0x7fffffffee89, /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/cloud-user/.local/bin:/home/cloud-user/bin

因此*2的原因是_环境变量和AT_EXECFN auxiliary vector value