Почему этот кусок кода может получить адрес переменной среды?

Учебник по разбивке стека 64-битного Linux-стека: Часть 1 использует метод получения адреса переменной среды для получения адреса переменной среды. Обязательное условие - сначала отключить ASLR через echo 0 > proc/sys/kernel/randomize_va_space,

Содержание сущности:

/*
 * 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 используется для корректировки имени программы?

Я предполагаю, что имя программы сохраняется в два раза выше стека.

Следующая диаграмма от 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 сохраняется один раз над строками среды. И если ./stackdump запускается из оболочки Bash, Bashell сохранит его в строках окружения с ключом _:

_

(Подчеркивание.) При запуске оболочки задайте абсолютный путь, используемый для вызова оболочки или сценария оболочки, выполняемого в том виде, как он был передан в среде или списке аргументов. Затем расширяется до последнего аргумента предыдущей команды после расширения. Также задайте полный путь, используемый для вызова каждой команды, выполненной и помещенной в среду, экспортированную в эту команду. При проверке почты этот параметр содержит имя почтового файла.

Строки среды находятся над стеком. Таким образом, имя программы сохраняется в другой раз выше стека.

2 ответа

В случае, если кто-то все еще задается вопросом, почему. Это связано с тем, что имя программы также хранится в имени переменной среды "_", кроме того, что оно помещается в стек перед всеми переменными среды.

Вы можете проверить это, подключив gdb к процессу и изучив содержимое стека под последними переменными среды. Предположим, что 0x7fffffffabcd является адресом последней переменной среды:

$ gdb -p <pid>

(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);
}

Во-первых, работает 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 вспомогательное векторное значение.

Другие вопросы по тегам