简体   繁体   English

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

[英]Why this piece of code can get environment variable address?

64-bit Linux stack smashing tutorial: Part 1 uses Get environment variable address gist to get environment variable address. 64位Linux栈粉碎教程:第1部分使用Get环境变量地址gist获取环境变量地址。 The prerequisite is to first disable ASLR via echo 0 > /proc/sys/kernel/randomize_va_space .先决条件是首先通过echo 0 > /proc/sys/kernel/randomize_va_space禁用 ASLR。

The content of the gist is:要点的内容是:

/*
 * 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);
}

Why *2 is used to adjust for program name?为什么用*2来调整程序名称?

My guess is that the program name is saved twice above the stack.我的猜测是程序名称在堆栈上方保存了两次。

在此处输入图片说明

The following diagram from https://lwn.net/Articles/631631/ gives more details:以下来自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           |       | |      | | |

In this diagram, ./stackdump is used to execute the program.在此图中,./ ./stackdump用于执行程序。 So I can see that the program name ./stackdump is saved once above environment strings.所以我可以看到程序名称./stackdump在环境字符串上方保存了一次。 And if ./stackdump is launched from Bash shell, Bashell will save it in environment strings with key _ :如果./stackdump从 Bash shell 启动,Bashell 会将其保存在带有键_环境字符串中:

_ _

(An underscore.) At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. (下划线。)在 shell 启动时,设置为用于调用在环境或参数列表中传递的正在执行的 shell 或 shell 脚本的绝对路径名。 Subsequently, expands to the last argument to the previous command, after expansion.随后,在扩展后扩展到上一个命令的最后一个参数。 Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command.还设置为用于调用在导出到该命令的环境中执行和放置的每个命令的完整路径名。 When checking mail, this parameter holds the name of the mail file.检查邮件时,此参数保存邮件文件的名称。

Environment strings are above the stack.环境字符串位于堆栈之上。 So the program name is saved another time above the stack.所以程序名再次保存在堆栈上方。

In case anyone is still wondering why.如果有人仍然想知道为什么。 This is because the program name is also stored in an environment variable name "_" beside being pushed on the stack before all the environment variables.这是因为除了在所有环境变量之前被压入堆栈之外,程序名称还存储在环境变量名称“_”中。

You can check this by attaching gdb to the process and examine the stack contents below the last environment variables.您可以通过将 gdb 附加到进程并检查最后一个环境变量下方的堆栈内容来检查这一点。 Suppose 0x7fffffffabcd is the address of the last environment variable:假设 0x7fffffffabcd 是最后一个环境变量的地址:

$ gdb -p <pid>

(gdb) x/20s 0x7fffffffabcd

The program name stored in argv[0] doesn't affect the environment variable's addresses because it is placed on top of the last environment variable on the stack.存储在argv[0]中的程序名称不会影响环境变量的地址,因为它位于堆栈中最后一个环境变量的顶部。

Save the following code as stackdump.c :将以下代码另存为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);
}

First, running gcc -o stackdump stackdump.c to compile the code.首先,运行gcc -o stackdump stackdump.c来编译代码。 Second, execute echo 0 > proc/sys/kernel/randomize_va_space .其次,执行echo 0 > proc/sys/kernel/randomize_va_space Third, running ./stackdump zero one two to give:第三,运行./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

Three copies of ./stackdump are in the program's address space as shown above. ./stackdump三个副本位于程序的地址空间中,如上所示。 Two of them have a higher address than PATH's as shown below:其中两个的地址比 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

So the reason for *2 is the _ environment variable and AT_EXECFN auxiliary vector value .所以*2的原因是_环境变量和AT_EXECFN辅助向量值

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM