Почему значение LD_PRELOAD в стеке
Я изучаю переполнение буфера и решаю некоторые варгеймы. Возникла проблема, что для всей стековой памяти над буфером установлено значение 0, кроме адреса возврата main, который будет:
buffer
[0000000...][RET][000000...]
и я могу переписать этот RET. Поэтому я нашел несколько советов для решения этой проблемы. Это было использовать LD_PRELOAD. Некоторые люди говорят, что значение LD_PRELOAD находится где-то в стеке, а не только в области переменных стека среды. Итак, я установил LD_PRELOAD, поискал его и нашел с помощью gdb.
$ export LD_PRELOAD=/home/coffee/test.so
$ gdb -q abcde
(gdb) b main
Breakpoint 1 at 0x8048476
(gdb) r
Starting program: /home/coffee/abcde
Breakpoint 1, 0x8048476 in main ()
(gdb) x/s 0xbffff6df
0xbffff6df: "@èC\001@/home/coffee/test.so"
(gdb) x/s 0xbffffc59
0xbffffc59: "LD_PRELOAD=/home/coffee/test.so"
(gdb) q
The program is running. Exit anyway? (y or n) y
$
Так что есть! Теперь я знаю, что значение LD_PRELOAD находится в стеке ниже буфера, и теперь я могу его использовать!
Но мне интересно, почему LD_PRELOAD загружается по этому адресу памяти. Значение также находится в области переменных среды стека!
Какова цель этого? Благодарю.
1 ответ
Код для изучения макета стека:
#include <inttypes.h>
#include <stdio.h>
// POSIX 2008 declares environ in <unistd.h> (Mac OS X doesn't)
extern char **environ;
static void dump_list(const char *tag, char **list)
{
char **ptr = list;
while (*ptr)
{
printf("%s[%d] 0x%.16" PRIXPTR ": %s\n",
tag, (ptr - list), (uintptr_t)*ptr, *ptr);
ptr++;
}
printf("%s[%d] 0x%.16" PRIXPTR "\n",
tag, (ptr - list), (uintptr_t)*ptr);
}
int main(int argc, char **argv, char **envp)
{
printf("%d\n", argc);
printf("argv 0x%.16" PRIXPTR "\n", (uintptr_t)argv);
printf("argv[argc+1] 0x%.16" PRIXPTR "\n", (uintptr_t)(argv+argc+1));
printf("envp 0x%.16" PRIXPTR "\n", (uintptr_t)envp);
printf("environ 0x%.16" PRIXPTR "\n", (uintptr_t)environ);
dump_list("argv", argv);
dump_list("envp", envp);
return(0);
}
С программой, скомпилированной как x
Я запустил его в санированной среде:
$ env -i HOME=$HOME PATH=$HOME/bin:/bin:/usr/bin LANG=$LANG TERM=$TERM ./x a bb ccc
4
argv 0x00007FFF62074EC0
argv[argc+1] 0x00007FFF62074EE8
envp 0x00007FFF62074EE8
environ 0x00007FFF62074EE8
argv[0] 0x00007FFF62074F38: ./x
argv[1] 0x00007FFF62074F3C: a
argv[2] 0x00007FFF62074F3E: bb
argv[3] 0x00007FFF62074F41: ccc
argv[4] 0x0000000000000000
envp[0] 0x00007FFF62074F45: HOME=/Users/jleffler
envp[1] 0x00007FFF62074F5A: PATH=/Users/jleffler/bin:/bin:/usr/bin
envp[2] 0x00007FFF62074F81: LANG=en_US.UTF-8
envp[3] 0x00007FFF62074F92: TERM=xterm-color
envp[4] 0x0000000000000000
$
Если вы внимательно изучите это, вы увидите, что argv
аргумент main()
является началом серии указателей на строки, идущие дальше по стеку; envp
(необязательный третий аргумент main()
на машинах POSIX) совпадает с глобальной переменной environ
а также argv[argc+1]
, а также является началом серии указателей на строки вверх по стеку; и строки, на которые указывает argv
а также envp
указатели следуют за двумя массивами.
Это макет в Mac OS X (10.7.5, если он имеет значение, что, вероятно, нет), но я вполне уверен, что вы найдете такой же макет в других Unix-подобных системах.