Перед выполнением main()
ОС должна выполнить кучу других операций, чтобы правильно настроить среду, прежде чем управление выполнением будет обработано вашим приложением.Таким образом, большая часть того, что находится в стеке на данный момент, является мусором, оставшимся от предыдущих операций.
Непосредственно перед выполнением main()
можно ожидать, что в стеке также будут найдены argc
и argv
.
РЕДАКТИРОВАТЬ:
Комментарий от пользователя заставил меня пройти через процесс отладки приложения сборки в gdb и проверки стекачтобы сделать резервную копию заявления, сделанного мной в исходном ответе.
Поэтому, пожалуйста, рассмотрите следующий код сборки, написанный в nasm :
section .data
mymsg db "hello, world", 0xa ; string with a carriage-return
mylen equ $-mymsg ; string length in bytes
section .text
global mystart ; make the main function externally visible
mystart:
; prepare the arguments for syscall write()
push dword mylen ; msg length
push dword mymsg ; msg to write
push dword 1 ; file descriptor number
; call write()
mov eax, 0x4 ; 0x4 identifies syscall write()
sub esp, 4 ; OS X (and BSD) syscalls needs "extra space" on stack
int 0x80 ; trigger the call
; clean up the stack
add esp, 16 ; 3 args * 4 bytes/arg + 4 bytes extra space = 16 bytes
; prepare argument for syscall exit()
push dword 0 ; exit status returned to the operating system
; call exit()
mov eax, 0x1 ; 0x1 identifies syscall exit()
sub esp, 4 ; OS X (and BSD) system calls needs "extra space" on stack
int 0x80 ; trigger the call
Я скомпилировал это в Mac OS Xс:
nasm -f macho -o hello.o hello.nasm
ld -o hello -e mystart hello.o
Как вы, вероятно, можете сказать по исходному коду, запуск приложения определяется как mystart
, и он не принимает никаких параметров.
Теперь,давайте сделаем это исследование немного более захватывающим, открыв эту программу в gdb :
gdb ./hello
После загрузки gdb в образовательных целях важно установить cmdПараметр линии для этого приложения, хотя яt не было написано, чтобы принять что-либо.
set args deadbeef
Приложение все еще не запущено на данный момент.Нам нужно установить точку останова в начале функции main , чтобы можно было проверить стек, чтобы увидеть, что происходит, прежде чем наше приложение начнет выполнять свой собственный код:
break mystart
Выполните команду run
на GDB, чтобы запустить приложение и прервать выполнение.Теперь мы можем проверить стек с:
x/20xw $esp
выходами:
(gdb) x/20xw $esp
0xbffff8cc: 0x00002000 0x00000000 0x00000002 0xbffff96c
0xbffff8dc: 0xbffff98b 0x00000000 0xbffff994 0xbffff9b0
0xbffff8ec: 0xbffff9c1 0xbffff9d1 0xbffffa0b 0xbffffa40
0xbffff8fc: 0xbffffa5b 0xbffffa86 0xbffffa97 0xbffffaad
0xbffff90c: 0xbffffad8 0xbffffafa 0xbffffb06 0xbffffb28
Да, сэр, эта команда печатает содержимое стека.Он gdb показывает 20 слов в шестнадцатеричном формате, начиная с адреса, сохраненного в регистре $esp
.
Давайте посмотрим, $esp
фактически указывает на 0xbffff8cc
, хорошо, но исследуемчто хранится по этому адресу памяти, показывает другой адрес: 0x00002000
.На что это указывает ???
(gdb) x/20sw 0x00002000
0x2000 <mymsg>: "hello, world\n"
Не шокер, верно ?!Итак, давайте посмотрим, на что указывают некоторые другие адреса таблицы:
(gdb) x/1sw 0xbffff96c
0xbffff96c: "/Developer/workspace/asm/hello"
Wow.На самом деле это имя и путь исходного приложения, хранящиеся прямо в стеке!Круто, давайте перейдем к следующему интересному адресу таблицы:
(gdb) x/1sw 0xbffff98b
0xbffff98b: "deadbeef"
Джекпот!Аргумент строки cmd, который мы передали при выполнении нашего приложения, также был сохранен в стеке.Итак, как я уже говорил ранее, среди мусора, хранящегося в стеке перед выполнением вашего приложения, вы также можете найти параметры строки cmd, которые использовались для выполнения приложения, даже когда функция main()
приложения имеет значение void
и не выполняетне беру никаких параметров.