Почему этот кусок кода может получить адрес переменной среды?
Учебник по разбивке стека 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
вспомогательное векторное значение.