Могу ли я выполнить код, который находится в сегменте данных (двоичный файл ELF)?

На пути к пониманию двоичных файлов (разметка виртуальной памяти, выполнение... и т. д.) я написал код C, который объявляет глобальную строку, содержащую байты исполняемого кода, затем я перезаписал адрес возврата из функции main() на этот исполняемый код, используя простой трюк, объявляя указатель (PTR) в main(), который представляет собой локальную область памяти, зарезервированную в стеке 2 СЛОВА далеко от адреса возврата из main(), поэтому все, что я делаю, это присваиваю адрес адреса возврата к этому указателю (PTR=(int*)&PTR+2), а затем перезаписать содержимое этого адреса адресом исполняемого кода (статической строкой).

Теперь дилемма заключается в том, что всякий раз, когда я компилирую и выполняю, я получаю ошибка сегментации. Исполняемый код не использует память input/output (это просто набор NOPs).

Используя GDB, я убедился, что процесс работает идеально: адрес возврата меняется на адрес строки, но возврата никогда не происходит.

Все, что я знаю, это то, что исполняемый код сопоставляется с страницы в виртуальной памяти помечены RW (сегменты .data и .bss), поэтому, возможно, нет способа выполнить такое выполнение кода, если только код не вводится в область executable памяти (страницы, помеченные RE). Это моя теория по этому вопросу, я приглашаю вас дать более подробную информацию.

char code[]="\x90\x90\x90\x90\x90\x90\x90\x90"; //a static string contains executable code

int main()
{
int *return_address; //Pointer to the return address - uninitialized
return_address = (int *)&return_address + 2; //Initializing the return address - according to stack layout
(*return_address) = (int)code; //Overwriting the return address with the code's address
}

person Karim Manaouil    schedule 30.06.2016    source источник
comment
Возможно, вы захотите вызвать системный вызов mprotect с добавленным флагом PROT_EXEC... На многих архитектурах (также x86) нет разрешения x на странице данных, проверьте карты памяти в Linux, прочитав файл /proc/$PID/maps, пока приложение запущено. работает (остановлен в gdb), где $PID — pid целевого процесса. Вы увидите сегмент data/bss, загруженный с разрешением rw.   -  person osgx    schedule 30.06.2016
comment
@osgx Это точно то же самое, о чем я догадался, допускают ли более старые ядра Linux (например, до 2007 года) такое выполнение?   -  person Karim Manaouil    schedule 30.06.2016
comment
ты? Вы можете проверить файл карты на старом ядре. Существует вики-страница по технологии: en.wikipedia.org/wiki/Executable_space_protection и бит NX / Битовая технология XD может потребоваться в ЦП и ядре, чтобы... добавить бит «Исполняемый» к переводам аппаратного обеспечения на уровне страниц (ОС может иметь 3-битные разрешения rwx, но аппаратное обеспечение не имеет бита x). PAE также должен быть включен в 32-битном режиме x86. (подробная информация находится здесь en.wikipedia.org/wiki/NX_bit)   -  person osgx    schedule 30.06.2016
comment
afr0ck, а какой у тебя реальный вопрос? Хотите более подробные ссылки?   -  person osgx    schedule 30.06.2016
comment
Ядра Linux › 2.3 поддерживают полный PAE с битом XD, поэтому мне действительно нужно очень старое ядро ​​​​Linux, чтобы сделать такую ​​​​попытку. Кстати спасибо за помощь. @osgx   -  person Karim Manaouil    schedule 30.06.2016
comment
Вопрос заключался в следующем: могу ли я выполнить приведенную выше программу без ошибки сегментации?... и это оказалось невозможным из-за защиты памяти!   -  person Karim Manaouil    schedule 30.06.2016
comment
afr0ck, есть инструменты для изменения настроек linux для одного приложения, например execstack tool (linux.die. net/man/8/execstack; стек можно переключить на rwx; см. статью 2004 г.). ядра ›2.3 могут поддерживать PAE, но в то же время PAE может быть не включен в конфигурации ядра (во время сборки; был вариант ядра linux-generic-pae).   -  person osgx    schedule 30.06.2016
comment
компоновщик может изменить флаги в разделах: stackoverflow.com/questions/7863200/   -  person osgx    schedule 30.06.2016
comment
Это было буквально полезно, спасибо @osgx.   -  person Karim Manaouil    schedule 30.06.2016
comment
Если вы хотите закрыть вопрос как решенный, вы можете (или не можете) нажать V-образную кнопку слева от правильного ответа (принять его). Или вы можете подождать кого-то с лучшим ответом.   -  person osgx    schedule 30.06.2016


Ответы (1)


я получаю ошибку сегментации.

Это аппаратный контроль предотвращения выполнения данных (https://en.wikipedia.org/wiki/Executable_space_protection#Linux) - вы не можете просто перейти на страницу данных, если в таблицах страниц не установлен бит 'x' (выполнение). Отображения памяти со всеми битами перечислены в файлах /proc/$pid/maps / /proc/$pid/smaps как 'rwx' для записываемого кода, 'rw-' для данных без выполнения, 'r--' для данных только для чтения, 'r-x' для обычного кода.

Если вы хотите выполнить данные, вы должны вызвать системный вызов mprotect с флагом PROT_EXEC в разделе ваших данных, который должен быть кодом.

В мире x86 это было полностью реализовано как функция "NX bit" / "XD bit" в Pentium. 4 (Prescott) и новее (Core, Core2, Core i*, core m) / в Athlon 64 / Opteron и новее. Если ОС работает в 32-битном режиме, она должна включить PAE, чтобы этот бит был в таблице страниц. В режиме x86_64 (64-бит) всегда поддерживается бит NX/XD.

Первые варианты поддержки были добавлены в Linux примерно в 2004 году: http://linuxgazette.net/107/pramode.html

В 2007 у вас может быть устаревшее оборудование, старое ядро ​​или ядро ​​32-битного режима без PAE.

Информация о битах NX/XD: https://en.wikipedia.org/wiki/NX_bit

Иногда режим 'rwx' может быть запрещен, проверьте https://en.wikipedia.org/wiki/W%5EX.

Для систем до NX существовали решения, основанные на сегментных регистрах x86, чтобы частично отключить выполнение части пространства памяти.

Могу ли я выполнить указанную выше программу без ошибки сегментации?

Вы можете:

  • сделать страницу данных исполняемой, вызвав на ней mprotect с помощью PROT_READ|PROT_EXEC
  • сделать сегмент данных файла elf помеченным как исполняемый (необходимо глубоко взломать ld скрипты - по умолчанию в ld --verbose)
  • сделать все страницы, включая .data и исполняемый файл кучи (а не только стек)
    с помощью ld или gcc -z execstack
  • переместить шеллкод в текстовые данные файла elf
  • попробуйте отключить бит nx/xd в ядре (жестко; может потребоваться перекомпиляция)
  • используйте 32-битную ОС (ядро) без включенной опции PAE (опция времени сборки).
  • использовать старый процессор без NX/XD
person osgx    schedule 30.06.2016
comment
PS: кто-то может (но не должен, согласно закону) использовать spray для получения собственного кода, написанного в удаленном процессе на исполняемых страницах (en.wikipedia.org/wiki/JIT_spraying). Не будет работать на RISC и Gogole NaCL (у них нет кодирования инструкций переменной длины или перехода к середине инструкции) и на современных JIT с защитой от брызг. - person osgx; 30.06.2016
comment
страница ubuntus nx/xd: wiki.ubuntu.com/Security/Features#nx 64-битные и 32-битные ядра -server и -generic-pae скомпилированы с адресацией PAE. Начиная с Ubuntu 9.10, эта защита частично эмулируется для процессоров без NX при работе на 32-битном ядре. Начиная с Ubuntu 11.04, настройки BIOS NX игнорируются ядром. И цветовая матрица. - person osgx; 30.06.2016
comment
Оказывается, использовать mprotect довольно просто, задавая зону через начальный адрес и длину. Единственное, на что следует обратить внимание, это то, что начальный адрес должен быть выровнен по странице (addr & ~4095). Обратите внимание, что GDB (по крайней мере, rr) сходит с ума, когда входит в вызов mprotect. - person fuujuhi; 14.02.2020