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

Учебное пособие по разрушению стека 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 сохранит его в строках окружения с ключом _:

_

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

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


person Jingguo Yao    schedule 08.11.2016    source источник
comment
Что именно вы спрашиваете? Код работает, потому что getenv получает адрес переменной среды, а вызов вашей программы также занимает место в стеке, поэтому вы соответствующим образом настраиваете указатель. Это в комментариях к коду.   -  person Jacob H    schedule 08.11.2016
comment
Насколько мне известно, обычно в стеке выделяется около 2 байтов на символ имени программы. Впервые я увидел этот фрагмент кода в книге Джона Эриксона Hacking: The Art of Exploitation. Я предлагаю прочитать больше там или изучить ядро ​​Linux, чтобы понять, как стек выглядит в памяти.   -  person Jacob H    schedule 08.11.2016
comment
@JacobH да, код взят со страниц 147 и 148 книги Джона Эриксона Hacking: The Art of Exploitation, 2nd Edition. Но книга не объясняет, почему это работает.   -  person Jingguo Yao    schedule 08.11.2016
comment
Это в основном потому, что имя программы сохраняется дважды, один раз в самом верху стека, а второй раз как argv[0]. (Конечно, argv[0] может не быть именем программы, в зависимости от того, как программа была вызвана, поэтому имя программы должно находиться в стеке отдельно.) См., например, lwn.net/Articles/631631   -  person rici    schedule 08.11.2016


Ответы (2)


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

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

$ gdb -p <pid>

(gdb) x/20s 0x7fffffffabcd

Имя программы, хранящееся в argv[0], не влияет на адреса переменных среды, поскольку оно помещается поверх последней переменной среды в стеке.

person neiht    schedule 03.11.2017

Сохраните следующий код как 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 вспомогательное значение вектора< /а>.

person Jingguo Yao    schedule 07.11.2017