Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么这段代码可以获取环境变量地址?_C_Linux_Getenv - Fatal编程技术网

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

C 为什么这段代码可以获取环境变量地址?,c,linux,getenv,C,Linux,Getenv,用于获取环境变量地址。先决条件是首先通过echo 0>/proc/sys/kernel/randomize\u va\u 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. */ #incl

用于获取环境变量地址。先决条件是首先通过
echo 0>/proc/sys/kernel/randomize\u va\u 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);
}
在此图中,
/stackdump
用于执行程序。因此,我可以看到程序名
/stackdump
在环境字符串上方保存一次。如果从bashshell启动了
/stackdump
,则Bashell将使用键
\uu
将其保存在环境字符串中:

_

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


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

以防有人仍在想为什么。这是因为程序名也存储在一个环境变量名“\u1”中,而不是在所有环境变量之前被推送到堆栈上

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

$ gdb -p <pid>

(gdb) x/20s 0x7fffffffabcd
$gdb-p
(gdb)x/20s 0x7fffffffabcd

存储在
argv[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);
}
如上所示,程序的地址空间中有三份
/stackdump
。其中两个具有比路径更高的地址,如下所示:

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
的原因是
\u
环境变量和
AT\u EXECFN

您到底在问什么?代码之所以有效,是因为getenv获取环境变量的地址,而对程序的调用也会占用堆栈上的空间,因此需要相应地调整指针。据我所知,在堆栈上分配的程序名中,每个字符通常有2个字节。我看到这段代码的第一个地方是在黑客中:Jon Erickson的剥削艺术。我建议在那里多读一些,或者研究一下linux内核,以了解堆栈在内存中的外观。@JacobH是的,代码源于Jon Erickson的第二版《黑客:利用的艺术》第147页和第148页。但这本书并没有解释它为什么会工作,基本上是因为程序名存储了两次,一次在堆栈的最顶端,另一次作为argv[0]。(当然,argv[0]可能不是程序名,这取决于调用程序的方式,这就是为什么程序名需要单独位于堆栈上的原因。)例如,请参阅,
  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
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