При загрузке программы стек начинается со следующих значений (от старших адресов к младшим):
------------------------------------------------------------ Нулевой указатель ------------------------------------------------------------ Указатель на n-ю переменную среды (при наличии) ------------------------------------------------------------ Указатель на вторую переменную среды (при наличии) ------------------------------------------------------------ Указатель на первую переменную среды (при наличии) ------------------------------------------------------------ Нулевой указатель ------------------------------------------------------------ Указатель на n-й аргумент программы (при наличии) ------------------------------------------------------------ Указатель на второй аргумент программы (при наличии) ------------------------------------------------------------ Указатель на первый аргумент программы (при наличии) ------------------------------------------------------------ Указатель на имя файла программы -------------------------------------------------------------- Количество аргументов командной строки ---------------------------------------------------------------
Таким образом, даже если мы находимся в точке входа _start
, мы можем получить эту информацию в нашей программе.
Переменные среды хранятся в виде отдельных строк видаMYVAR=MYVAL
, где MYVAR
- имя переменной, а MYVAL
- ее значение.
Например, загрузим количество аргументов в регистр %rax, а указатель на имя файла - в регистр %rbx:
_start: movq 0(%rsp), %rax # количество аргументов movq 8(%rsp), %rbx # указатель на файл
Проблема при использовании подобных данных заключается в том, что нам может быть сложно вычислить длину строки (например, длину файла или длину переменных) для взаимодействии с ними, особенно на различных эмуляторов типа WSL.
При использовании библиотеки C (когда точкой входа в программу является функция main, а не метка _start) получение подобных данных можно упростить.
Так, первый параметр main — это количество аргументов командной строки (и, следовательно, он будет доступен в %rdi).
Второй параметр main — это указатель на массив указателей на строки аргументов командной строки, и этот указатель будет находиться в %rsi.
Наконец, мы можем получить переменные среды по отдельности, вызвав функцию getenv
, которая есть в в библиотеке языка Си, или получить разом все переменные с помощью
функции environ
.
Стоит учитывать, что хотя начало стека (растущего вниз в памяти) находится рядом с 0x00007ffffffffffff, Linux реализует некоторую рандомизацию для этого, чтобы хакеры не могли угадать, где на самом деле находится стек в памяти.