Как запустить elf файл linux

Обновлено: 07.07.2024

Не получается запустить бинарный файл скомпилированный из qt.
Пробовал запускать через интерпретатор ld-linux-x86-64.so.2 получил - cannot open shared object file. Подскажите, как можно узнать, какие библиотеки и зависимости нужны для запуска файла?

chmod +x binary.elf, но это дикость, обычно такое не нужно.

как можно узнать, какие библиотеки и зависимости нужны для запуска файла?

chmod +x binary.elf, но это дикость, обычно такое не нужно.

не запускает так. у меня файл без разширения, небольшая программа, которую нужно протестировать

как теперь понять, какие библиотеки установлены у меня, а какие нет?

как теперь понять, какие библиотеки установлены у меня, а какие нет?

(not found) значит не установлена

Скорее всего надо пересобирать приложение, вангую версии библиотек не совпадают.

(not found) значит не установлена

нету такого, все установлены. из-за чего еще может не запускаться?

Скорее всего надо пересобирать приложение, вангую версии библиотек не совпадают.

нет такой возможности, я тестирую это приложение, и оно точно работает.

нету такого, все установлены. из-за чего еще может не запускаться?

Может она через dlopen грузит зависимости и проверяет их наличие.

проблема не в исправности приложения, а в том что на вашей машине и машине сборки разные версии библиотек (эта отвечает за загрузку/выгрузку динамических библиотек), потому даже если вы начнете подсовывать библиотеки с компа, на котором собиралось приложение, скорее всего ничего путного не выйдет. Выхода три:

  • установить виртуалку с точно такой же системой как на машине сборки.
  • пересобрать приложение из исходников на вашей
  • пересобрать приложение в режиме статической линковки
Silerus ★★★ ( 15.02.21 20:29:01 )
Последнее исправление: Silerus 15.02.21 20:29:50 (всего исправлений: 1)

Может она через dlopen грузит зависимости и проверяет их наличие.

можешь подсказать, как я могу это проверить? если честно я полный 0 в линуксе

Через strace наверно.


Тестирую на тех системах, где должно работать. Если нет – заводи багу разработчикам.

Собрать не только бинарь, но и опакетить его, указав в зависимостях нужные библиотеки и их версии.


Не получается запустить бинарный файл скомпилированный из qt.

Для начала попробуйте chmod +x you_file . Вы точно знаете где лежит этот самый you_file? Можете ещё заодно глянуть что за файл через file you_file , а то может оказаться что с самим по себе запускаемым файлом какой-нибудь косяк, типа он для другой архитектуры изначально собран.

Проверить какие библиотеки нужны можно:

Не запуская программу:

  • ldd you_file Это наиболее прямой путь.
  • nm you_file Это более извилистый путь. Показываются символы (имена функций) в библиотеках.
  • ltrace you_file Это позволяет перехватить библиотечные вызовы (какая библиотека и в какой момент вызывается).
  • strace you_file Это более извилистый путь, т.к. перехватывает по большей части системные вызовы (сисколлы) к ядру Linux.

P.S. ptrace, dtrace, dtruss не обсуждаем, т.к. это сравнительно редкие на практике «звери».

Moisha_Liberman ★★ ( 15.02.21 22:57:59 )
Последнее исправление: Moisha_Liberman 15.02.21 23:01:19 (всего исправлений: 2)

Запусти с трейсами динамического линкера


Тупо в лоб, запускаешь в терминале, программа сообщает, нет libxxx.so, добавляешь. Запускаешь….

Реальная история. Много лет пользовался mplayer, даже забыл уже, где я его взял, таскал из системы в систему. Был он 32битный. Решил запустить его в 64битной системе. Чисто в академических целях так и делал. Запускаю, читаю на что жалуется, добавляю, пишу враппер. Запускаю… Пришлось добавить порядка двухсот библиотек. Mplayer заработал. Для GUI-версии понадобилось к тому списку добавить еще пару десятков. Вот сейчас пишу, а он мне поет.

проблема не в исправности приложения, а в том что на вашей машине и машине сборки разные версии библиотек (эта отвечает за загрузку/выгрузку динамических библиотек), потому даже если вы начнете подсовывать библиотеки с компа, на котором собиралось приложение, скорее всего ничего путного не выйдет. Выхода три:

установить виртуалку с точно такой же системой как на машине сборки. пересобрать приложение из исходников на вашей пересобрать приложение в режиме статической линковки

Спасибо за совет! Но проблема оказалась не в этом, не совпадали версии qt, у меня стояла 5, нужна была 5.9 и выше. на 5.9 заработало


Бесфайловое распространение вредоносного ПО набирает популярность. Что не удивительно, ведь работа таких программ практически не оставляет следов. В этой статье мы не будем касаться техник выполнения программ в памяти Windows. Сконцентрируемся на GNU/Linux. Linux по праву доминирует в серверном сегменте, обитает на миллионах встраиваемых устройств и обеспечивает работу подавляющего большинства веб-ресурсов. Далее мы сделаем небольшой обзор возможностей исполнения программ в памяти и продемонстрируем что это возможно даже в затруднительных условиях.

Техники бесфайлового выполнения программ скрытны, обнаружить и отследить их использование крайне сложно. Средства контроля целостности файловой системы не предупредят администратора, поскольку никаких операций записи на диск либо изменения файлов на диске не происходит. Антивирусное ПО (которым часто пренебрегают пользователи *nix) зачастую не отслеживает память программ после запуска. К тому же во многих дистрибутивах GNU/Linux сразу после установки доступен широчайший спектр всевозможных утилит отладки, интерпретаторов, компиляторов языков программирования и библиотек для них. Все это создает прекрасные условия для использования техник скрытного, безфайлового выполнения программ. Но помимо плюсов их применения есть и недостатки — эти программы не переживают обесточивание или перезагрузку целевого хоста. Но пока хост запущен, программа работает.

В случае с Windows, злоумышленники активно используют предустановленный в системе Powershell для того, чтобы загрузить и тут же выполнить код. Эти техники получили широкое распространение в том числе и благодаря реализации в таких фреймворках как Powershell Empire, Powersploit и Metasploit Framework.

В большинстве случаев дистрибутивы Linux, установленные на хостах, имеют заранее предустановленный набор программного обеспечения. "Из коробки", как правило, доступны интерпретаторы языков программирования: Python, Perl, компилятор языка C. На хостинг-площадках в довесок присутствует PHP. Это условие обеспечивает возможность выполнить код средствами этих языков.

В Linux у нас есть несколько широко известных вариантов исполнения кода в памяти.
Проще всего — воспользоваться заранее смонтированной в файловую систему областью разделяемой памяти (shared memory).

Поместив исполняемый файл в каталог /dev/shm или /run/shm, можно добиться его выполнения в непосредственно в памяти, учитывая, что эти каталоги — не что иное, как смонтированная на файловую систему область оперативной памяти. Но их можно просмотреть с помощью ls как и любой другой каталог. Да и как правило эти каталоги монтируются с флагом noexec, и выполнение программ в этих директориях доступно только суперпользователю. Значит, чтобы быть чуть более незаметным, нужно что-то еще.

Более примечателен системный вызов memfd_create(2). Этот системный вызов работает примерно как malloc(3), но возвращает не указатель на область памяти, а файловый дескриптор на анонимный файл, который виден в файловой системе только как ссылка в /proc/PID/fd/ , по которой его можно выполнить с помощью execve(2).
Вот что говорит страница руководства по использованию системного вызова memfd_create(на русском):

"Имя, указанное в name , используется в качестве имени файла и будет показываться как цель соответствующей символьной ссылки в каталоге. /proc/self/fd/ . Отображаемое имя всегда начинается с memfd: и служит только для отладки. Имена не влияют на поведение файлового дескриптора, и поэтому несколько файлов могут иметь одно имя без каких-либо последствий."

Пример использования memfd_create() для языка C:

Код выше использует memfd , создает дочерний процесс, направляет его вывод во временный файл, ожидает завершения дочернего процесса и считывает его вывод из временного файла. Обычно для перенаправления вывода одной программы на ввод другой в *nix используют pipe "|".

Возможность использования syscall() есть и в интерпретируемых языках, таких как perl, python, etc… Далее рассмотрим один из возможных сценариев и продемонстрируем возможность загрузки исполняемых файлов в память с помощью memfd_create() .

Допустим, мы имеем точку входа в виде command injection.
Нам потребуется способ выполнять системные вызовы в целевой системе.
В perl с этим нам поможет функция syscall().
Нам также понадобится способ записать наш ELF напрямую в память как содержимое анонимного файла.
Для этого мы поместим наш ELF прямо в тело скрипта, который в свою очередь будет передан на целевую систему через доступный command injection. В качестве альтернативы также можно загрузить исполняемый файл по сети.
Но перед этим стоит сделать оговорку. Нам надо знать версию ядра linux на целевом хосте, поскольку необходимый нам системный вызов memfd_create() доступен только с версий 3.17 и выше.

Давайте поближе познакомимся с memfd_create() и execve()

Для нашего анонимного файла мы будем использовать константу MFD_CLOEXEC , которая "устанавливает флаг close-on-exec (FD_CLOEXEC) для нового открытого файлового дескриптора." Это значит, наш файловый дескриптор автоматически закроется после того, как мы выполним наш ELF с помощью execve()

Теперь у нас есть все необходимые числовые значения, и мы можем написать на языке Perl аналог memfd_create(name, MFD_CLOEXEC) из С.
Еще нам необходимо будет придумать имя файла, которое будет отображаться в /memfd:
Оптимально будет выбрать имя, похожее на [:kworker] либо другое, не вызывающее подозрения.
Для примера в параметр имени мы будем передавать пустую строку:

Теперь у нас есть дескриптор анонимного файла в $fd и нам надо записать ELF в этот файл.
Функция open() в perl обычно используется для открытия файлов, однако с помощью конструкции >&=FD , передавая этой функции дескриптор вместо имени файла, мы превращаем уже открытый файловый дескриптор в file handle.
Нам бы так же пригодился autoflush[] :

Теперь у нас есть дескриптор, ссылающийся на анонимный файл.

Далее нам необходимо преобразовать наш исполняемый файл в данные, которые можно поместить в тело Perl-скрипта.
Для этого выполняем:

Мы получим много-много подобных строк:

Выполнив их, мы поместим наш исполняемый файл в память. Нам останется лишь запустить его.

Опционально мы можем использовать fork(). Это вовсе не обязательно. Но если мы хотим не просто запустить ELF и убить процесс, нам нужно будет использовать fork() .
В общем случае создание дочернего процесса в perl выглядит примерно так:

Полезность fork() еще и в том, что, вызвав его вместе с setsid(2), можно отделить дочерний процесс от родительского и дать родителю завершиться:

Теперь мы можем запустить ELF во множестве процессов.

Execve()

Execve() — системный вызов, позволяющий нам выполнять программу. Perl предоставляет нам подобный функционал посредством функции Exec(), которая работает точно так же как вышеупомянутый системный вызов, но имеет куда более простой и удобный синтаксис.
Нам нужно передать в exec() две вещи: файл, который мы хотим выполнить (наш заранее загруженный в память ELF), и имя процесса в качестве одного из передаваемых аргументов. Обычно имя процесса соответствует имени исполняемого файла. Но поскольку в листинге процессов мы будем видеть /proc/PID/fd/3 , мы назовем наш процесс как-нибудь по-другому.
Синтаксис exec() выглядит следующим образом:

Python

По аналогии с Perl-вариантом нам нужно сделать следующее:

  • с помощью системного вызова memfd_create() создать в памяти анонимный файл
  • записать в этот файл исполняемый ELF
  • выполнить его и опционально выполнить несколько раз с помощью fork()

В случае с python, чтобы вызвать syscall нам понадобится стандартный модуль ctypes и os для записи и выполнения файла и управления процессом. Все полностью аналогично варианту на perl.
В коде выше мы записываем в память файл, заранее размещенный в каталоге /tmp/ . Однако ничто не мешает нам загрузить файл с веб-сервера.

На данном этапе мы уже можем использовать perl и python. Интерпретаторы этих языков по умолчанию установлены во многих ОС. Но самое интересное, как всегда, впереди.
Если по какой-то причине нам недоступны интерпретаторы perl или python, было бы здорово использовать PHP. Этот язык очень популярен среди веб-разработчиков. И если уж мы нашли возможность выполнения кода в веб-приложении, с большой вероятностью нас встретит именно интерпретатор PHP.

К сожалению, php не имеет встроенных механизмов для вызова syscall .
Нам на глаза попался пост от Beched'a на форуме rdot (Спасибо Beched!), который перезаписывает через procfs /proc/self/mem в памяти текущего процесса вызов функции open на system и за счет этого обходит disable_functions .
Мы же применили этот трюк для перезаписи функции на наш код, который будет вызывать нужные системные вызовы.
Передавать syscall интерпретатору php мы будем в виде шеллкода на assembler'е.
Системные вызовы необходимо будет передать через последовательность команд.
Приступим к написанию PHP-сценария. Далее будет много магии.

Сначала обозначим необходимые параметры:

Обозначим сдвиг — верхнее и нижнее значение в памяти, куда позже поместим наш шеллкод:

Далее функция, с помощью которой "распаковывается" бинарный файл. Для этого преобразуем бинарные данные в десятеричное представление c помощью функции hexdex() из бинарных данных bin2hex() в обратном порядке (для помещения в память):

Далее разбирается файл формата ELF для получения смещений:

Дополнительно выведем информацию об установленной версии PHP:

Сверяем разрядность операционной системы и версию ядра Linux:

Для того, чтобы обойти ограничения disable_functions , скрипт переписывает на ходу адрес функции open@plt . Мы внесли несколько дополнений к коду beched'a, и теперь можем поместить в память наш шеллкод.

Сначала необходимо найти сдвиг в бинарном файле самого интерпретатора PHP, для этого обращаемся к /proc/self/exe и разбираем исполняемый файл с помощью вышеописанной функции parseelf() :

Находим адрес функции open() :

Теперь можно перейти непосредственно к загрузке нашего исполняемого файла.
Сначала создаем анонимный файл:

Записываем нагрузку в анонимный файл:

Ищем номер файлового дескриптора:

Далее пишем в стек путь до исполняемого файла:

И аргументы для запуска, передаваемые исполняемой программе:

Далее с помощью вызова fork() выполняем нашу полезную нагрузку:

Шеллкод

Под шеллкодом обычно подразумевают последовательность байт, которую помещают в память, а затем выполняют — обычно в контексте работы другой программы, с помощью атак на переполнение буфера и прочих. В нашем же случае шеллкод не возвращает нам приглашение командной строки удаленного сервера (собственно Шелл), но позволяет выполнить необходимые нам команды.

Чтобы получить необходимую последовательность байт, можно либо написать код на языке C, затем перевести в язык ассемблера, либо написать на ассемблере с нуля.

Разберемся что же скрывается за последовательностью байт из листингов выше.

Запуск нашей программы начинается c fork . 57 это номерное значение идентификатора системного вызова для 64-х разрядных систем. Таблицу можно найти тут.

Далее вызываем setsid (нумерной идентификатор 112) для преобразования дочернего процесса в родительский:

Затем выполняем еще один fork :

Затем выполняем уже знакомый вам execve() :

И завершаем процесс с помощью exit() (60):

Таким образом мы выполнили замену кода функции open() на ходу. Наш исполняемый файл был помещен в память и выполнен средствами интерпретатора PHP. Системные вызовы представлены в виде шеллкодов.

Metasploit Framework

В качестве компиляции вышеописанных техник, мы подготовили модуль для MSF.

Чтобы добавить его в Metasploit, достаточно просто скопировать файл модуля в директорию $HOME/.msf4/module/post/linux/manage/download_exec_elf_in_memory.rb а затем выполнить команду reload_all в консоли фреймворка.
Чтобы использовать наш модуль вводим use post/linux/manage/download_exec_elf_in_memory (или другой путь, в зависимости от того, в какую директорию был помещен файл модуля)
Перед тем как использовать его, необходимо задать необходимые опции. Список опций выводим командой show options

ARGS — Аргументы для исполняемого файла

FILE — путь до исполняемого файла. В нашем случае это Netcat

NAME — имя процесса. Обозвать его можно как угодно. Например, для пущей незаметности это может быть kworker:1 ну или в целях демонстрации что-нибудь шуточное, например KittyCat

SESSION — сессия meterpreter. Подразумевается, что этот модуль будет использован в целях пост-эксплуатации.

VECTOR — метод, которым будет достигнуто исполнение программы в памяти, параметр не обязательный, если он будет пуст скрипт сам установит наличие нужных интерпретаторов. На данный момент поддерживаются PHP, Python или Perl.

Выполняем с помощью команды exploit или run

Вместо заключения.

Бесфайловая загрузка ELF-файлов в Linux — полезная техника при проведении тестирования на проникновение. Это достаточно бесшумный способ, способный противостоять широкому спектру средств антивирусной защиты, систем контроля целостности и мониторинговых систем следящих за изменением содержимого жесткого диска. Таким образом можно легко поддержать доступ к целевой системе, оставив при этом минимум следов.
В этой статье мы использовали интерпретируемые языки программирования, часто установленные по умолчанию в дистрибутивах Linux, прошивках встраиваемого оборудования, роутерах и мобильных устройствах. Отдельно хочется поблагодарить автора данной статьи, который вдохновил нас на этот ресерч.

Есть в мире вещи, которые мы принимаем как нечто само собой разумеющееся, хотя они являются истинными шедеврами. Одними из таких вещей являются утилиты Linux, такие, как ls и ps. Хотя они обычно воспринимаются как простые, это оказывается далеко не так, если мы заглянем внутрь. И таким же оказывается ELF, Executable and Linkable Format. Формат файлов, который используется повсеместно, но мало кто его понимает. Это краткое руководство поможет вам достичь понимания.


Прочтя это руководство, вы изучите:

  • Зачем нужен формат ELF и для каких типов файлов он используется
  • Структуру файла ELF и детали его формата
  • Как читать и анализировать бинарное содержимое файла ELF
  • Какие инструменты используются для анализа бинарных файлов

Что представляет собой файл ELF?

ELF — это сокращение от Executable and Linkable Format (формат исполняемых и связываемых файлов) и определяет структуру бинарных файлов, библиотек, и файлов ядра (core files). Спецификация формата позволяет операционной системе корректно интерпретировать содержащиеся в файле машинные команды. Файл ELF, как правило, является выходным файлом компилятора или линкера и имеет двоичный формат. С помощью подходящих инструментов он может быть проанализирован и изучен.

Зачем изучать ELF в подробностях?

Перед тем, как погрузиться в технические детали, будет не лишним объяснить, почему понимание формата ELF полезно. Во-первых, это позволяет изучить внутреннюю работу операционной системы. Когда что-то пошло не так, эти знания помогут лучше понять, что именно случилось, и по какой причине. Также возможность изучения ELF-файлов может быть ценна для поиска дыр в безопасности и обнаружения подозрительных файлов. И наконец, для лучшего понимания процесса разработки. Даже если вы программируете на высокоуровневом языке типа Go, выа всё равно будет лучше знать, что происходит за сценой.

Итак, зачем изучать ELF?

  • Для общего понимания работы операционной системы
  • Для разработки ПО
  • Цифровая криминалистика и реагирование на инциденты (DFIR)
  • Исследование вредоносных программ (анализ бинарных файлов)

От исходника к процессу

Какую бы операционную систему мы не использовали, необходимо каким-то образом транслировать функции исходного кода на язык CPU — машинный код. Функции могут быть самыми базовыми, например, открыть файл на диске или вывести что-то на экран. Вместо того, чтобы напрямую использовать язык CPU, мы используем язык программирования, имеющий стандартные функции. Компилятор затем транслирует эти функции в объектный код. Этот объектный код затем линкуется в полную программу, путём использования линкера. Результатом является двоичный файл, который может быть выполнен на конкретной платформе и конкретном типе CPU.

Прежде, чем начать

Этот пост содержит множество команд. Лучше запускать их на тестовой машине. Скопируйте существующие двоичные файлы, перед тем, как запускать на них эти команды. Также мы напишем маленькую программу на С, которую вы можете скомпилировать. В конечном итоге, практика — лучший способ чему-либо научиться.

Анатомия ELF-файла

Распространённым заблуждением является то, что файлы ELF предназначены только для бинарных или исполняемых файлов. Мы уже сказали, что они могут быть использованы для частей исполняемых файлов (объектного кода). Другим примером являются файлы библиотек и дампы ядра (core-файлы и a.out файлы). Спецификация ELF также используется в Linux для ядра и модулей ядра.


Структура

В силу расширяемости ELF-файлов, структура может различаться для разных файлов. ELF-файл состоит из:


заголовок ELF

Как видно на скриншоте, заголовок ELF начинается с «магического числа». Это «магическое число» даёт информацию о файле. Первые 4 байта определяют, что это ELF-файл (45=E,4c=L,46=F, перед ними стоит значение 7f).

Заголовок ELF является обязательным. Он нужен для того, чтобы данные корректно интерпретировались при линковке и исполнении. Для лучшего понимания внутренней работы ELF-файла, полезно знать, для чего используется эта информация.

Класс

После объявления типа ELF, следует поле класса. Это значение означает архитектуру, для которой предназначен файл. Оно может равняться 01 (32-битная архитектура) или 02 (64-битная). Здесь мы видим 02, что переводится командой readelf как файл ELF64, то есть, другими словами, этот файл использует 64-битную архитектуру. Это неудивительно, в моей машине установлен современный процессор.

Данные

Далее идёт поле «данные», имеющее два варианта: 01 — LSB (Least Significant Bit), также известное как little-endian, либо 02 — MSB (Most Significant Bit, big-endian). Эти значения помогают интерпретировать остальные объекты в файле. Это важно, так как разные типы процессоров по разному обрабатывают структуры данных. В нашем случае используется LSB, так как процессор имеет архитектуру AMD64.

Эффект LSB становится видимым при использовании утилиты hexdump на бинарном файле. Давайте посмотрим заголовок ELF для /bin/ps.

Мы видим, что пары значений другие, из-за интерпретации порядка данных.

Версия

Затем следует ещё одно магической значение «01», представляющее собой номер версии. В настоящее время имеется только версия 01, поэтому это число не означает ничего интересного.

OS/ABI

Каждая операционная система имеет свой способ вызова функций, они имеют много общего, но, вдобавок, каждая система, имеет небольшие различия. Порядок вызова функции определяется «двоичным интерфейсом приложения» Application Binary Interface (ABI). Поля OS/ABI описывают, какой ABI используется, и его версию. В нашем случае, значение равно 00, это означает, что специфические расширения не используются. В выходных данных это показано как System V.

Версия ABI

При необходимости, может быть указана версия ABI.

Машина

Также в заголовке указывается ожидаемый тип машины (AMD64).

Поле типа указывает, для чего предназначен файл. Вот несколько часто встречающихся типов файлов.

CORE (значение 4)
DYN (Shared object file), библиотека (значение 3)
EXEC (Executable file), исполняемый файл (значение 2)
REL (Relocatable file), файл до линковки (значение 1)

Смотрим полный заголовок

Хотя некоторые поля могут быть просмотрены через readelf, их на самом деле больше. Например, можно узнать, для какого процессора предназначен файл. Используем hexdump, чтобы увидеть полный заголовок ELF и все значения.


(вывод hexdump -C -n 64 /bin/ps)

Выделенное поле определяет тип машины. Значение 3e — это десятичное 62, что соответствует AMD64. Чтобы получить представление обо всех типах файлов, посмотрите этот заголовочный файл.

Хотя вы можете делать всё это в шестнадцатиричном дампе, имеет смысл использовать инструмент, который сделает работу за вас. Утилита dumpelf может быть полезна. Она показывает форматированный вывод, соответствующий заголовку ELF. Хорошо будет изучить, какие поля используются, и каковы их типичные значения.

Теперь, кгда мы объяснили значения этих полей, время посмотреть на то, какая реальная магия за ними стоит, и перейти к следующим заголовкам!

Данные файла

Помимо заголовка, файлы ELF состоят из трёх частей.

  • Программные заголовки или сегменты
  • Заголовки секций или секции
  • Данные

Заголовки программы

Файл ELF состоит из нуля или более сегментов, и описывает, как создать процесс, образ памяти для исполнения в рантайме. Когда ядро видит эти сегменты, оно размещает их в виртуальном адресном пространстве, используя системный вызов mmap(2). Другими словами, конвертирует заранее подготовленные инструкции в образ в памяти. Если ELF-файл является обычным бинарником, он требует эти программные заголовки, иначе он просто не будет работать. Эти заголовки используются, вместе с соответствующими структурами данных, для формирования процесса. Для разделяемых библиотек (shared libraries) процесс похож.



Программный заголовок в бинарном ELF-файле

Мы видим в этом примере 9 программных заголовков. Сначала трудно понять, что они означают. Давайте погрузимся в подробности.

GNU_EH_FRAME

Это сортированная очередь, используемая компилятором GCC. В ней хранятся обработчики исключений. Если что-то пошло не так, они используются для того, чтобы корректно обработать ситуацию.

GNU_STACK

Этот заголовок используется для сохранения информации о стеке. Интересная особенность состоит в том, что стек не должен быть исполняемым, так как это может повлечь за собой уязвимости безопасности.

Если сегмент GNU_STACK отсутствует, используется исполняемый стек. Утилиты scanelf и execstack показывают детали устройства стека.

  • dumpelf (pax-utils)
  • elfls -S /bin/ps
  • eu-readelf –program-headers /bin/ps

Секции ELF

Заголовки секции

Заголовки секции определяют все секции файла. Как уже было сказано, эта информация используется для линковки и релокации.

Секции появляются в ELF-файле после того, как компилятор GNU C преобразует код С в ассемблер, и ассемблер GNU создаёт объекты.

Как показано на рисунке вверху, сегмент может иметь 0 или более секций. Для исполняемых файлов существует четыре главных секций: .text, .data, .rodata, и .bss. Каждая из этих секций загружается с различными правами доступа, которые можно посмотреть с помощью readelf -S.

Содержит исполняемый код. Он будет упакован в сегмент с правами на чтение и на исполнение. Он загружается один раз, и его содержание не изменяется. Это можно увидеть с помощью утилиты objdump.

Инициализированные данные, с правами на чтение и запись.

.rodata

Инициализированные данные, с правами только на чтение. (=A).

Неинициализированные данные, с правами на чтение/запись. (=WA)

  • dumpelf
  • elfls -p /bin/ps
  • eu-readelf –section-headers /bin/ps
  • readelf -S /bin/ps
  • objdump -h /bin/ps

Группы секций

Некоторые секции могут быть сгруппированы, как если бы они формировали единое целое. Новые линкеры поддерживают такую функциональность. Но пока такое встречается не часто.

Хотя это может показаться не слишком интересным, большие преимущества даёт знание инструментов анализа ELF-файлов. По этой причине, обзор этих инструментов и их назначения приведён в конце статьи.

Статические и динамические бинарные файлы

Когда мы имеем дело с бинарными файлами ELF, полезно будет знать, как линкуются эти два типа файлов. Они могут быть статическими и динамическими, и это относится к библиотекам, которые они используют. Если бинарник «динамический», это означает, что он использует внешние библиотеки, содержащие какие-либо общие функции, типа открытия файла или создания сетевого сокета. Статические бинарники, напротив, включают в себя все необходимые библиотеки.

Если вы хотите проверить, является ли файл статическим или динамическим, используйте команду file. Она покажет что-то вроде этого:


Чтобы определить, какие внешние библиотеки использованы, просто используйте ldd на том же бинарнике:

Совет: Чтобы посмотреть дальнейшие зависимости, лучше использовать утилиту lddtree.

Инструменты анализа двоичных файлов

Если вы хотите анализировать ELF-файлы, определённо будет полезно сначала посмотреть на существующие инструменты. Существуют тулкиты для обратной разработки бинарников и исполняемого кода. Если вы новичок в анализе ELF-файлов, начните со статического анализа. Статический анализ подразумевает, что мы исследуем файлы без их запуска. Когда вы начнёте лучше понимать их работу, переходите к динамическому анализу. Запускайте примеры и смотрите на их реальное поведение.

Популярные инструменты

Radare2

Тулкит Radare2 создан Серджи Альваресом (Sergi Alvarez). Число 2 подразумевает, что код был полностью переписан по сравнению с первой версией. Сейчас он используется многими исследователями, для изучения работы кода.

Программные пакеты

Большинство Linux-систем имеют установленный пакет binutils. Другие пакеты могут помочь вам увидеть больше информации. Правильный тулкит упростит вашу работу, особенно если вы занимаетесь анализом ELF-файлов. Я собрал здесь список пакетов и утилит для анализа ELF-файлов.

elfutils
/usr/bin/eu-addr2line
/usr/bin/eu-ar – альтернатива ar, для создания и обработки архивных файлов
/usr/bin/eu-elfcmp
/usr/bin/eu-elflint – проверка на соответствие спецификациям gABI и psABI
/usr/bin/eu-findtextrel – поиск релокаций текста
/usr/bin/eu-ld – комбинирует объектный и архивные файлы
/usr/bin/eu-make-debug-archive
/usr/bin/eu-nm – показывает символы объектного и исполняемого файлов
/usr/bin/eu-objdump – показывает информацию из объектного файла
/usr/bin/eu-ranlib – создаёт индекс архивных файлов
/usr/bin/eu-readelf – показывает ELF-файл в читаемой форме
/usr/bin/eu-size – показывает размер каждой секции (text, data, bss, etc)
/usr/bin/eu-stack – показывает стек текущего процесса или дампа ядра
/usr/bin/eu-strings – показывает текстовые строки (как утилита strings)
/usr/bin/eu-strip – удаляет таблицу символов из файла ELF
/usr/bin/eu-unstrip – добавляет символы и отладочную информацию в бинарник
Примечание: пакет elfutils будет хорошим началом, он содержит большинство утилит для анализа

elfkickers
/usr/bin/ebfc – компилятор языка Brainfuck
/usr/bin/elfls – показывает программные заголовки и заголовки секций с флагами
/usr/bin/elftoc – преобразует бинарник в программу на С
/usr/bin/infect – утилита, инжектирующая дроппер, создаёт файл setuid в /tmp
/usr/bin/objres – создаёт объект из обычных или бинарных данных
/usr/bin/rebind – изменяет связывание и видимость символов в ELF-файлах
/usr/bin/sstrip – удаляет ненужные компоненты из ELF-файла
Примечание: автор пакета ELFKickers сфокусирован на манипулировании ELF-файлами, что позволяет вам получить больше информации при работе с «неправильными» ELF-бинарниками

pax-utils
/usr/bin/dumpelf – дамп внутренней структуры ELF
/usr/bin/lddtree – как ldd, с установкой уровня показываемых зависимостей
/usr/bin/pspax – выводит ELF/PaX информацию о запущенных процессах
/usr/bin/scanelf – широкий диапазон информации, включая подробности PaX
/usr/bin/scanmacho – показывает подробности бинарников Mach-O (Mac OS X)
/usr/bin/symtree – показывает символы в виде дерева
Примечание: некоторые утилиты в этом пакете могут рекурсивно сканировать директории, и идеальны для анализа всего содержимого директории. Фокус сделан на инструментах для исследования подробностей PaX. Помимо поддержки ELF, можно извлекать информацию из Mach-O-бинарников.


prelink
/usr/bin/execstack – можно посмотреть или изменить информацию о том, является ли стек исполняемым
/usr/bin/prelink – релоцирует вызовы в ELF файлах, для ускорения процесса

Часто задаваемые вопросы

Что такое ABI?

ABI — это Бинарный Интерфейс Приложения (Application Binary Interface) и определяет, низкоуровневый интерфейс между операционной системой и исполняемым кодом.

Что такое ELF?

ELF — это Исполняемый и Связываемый Формат (Executable and Linkable Format). Это спецификация формата, определяющая, как инструкции записаны в исполняемом коде.

Как я могу увидеть тип файла?

Используйте команду file для первой стадии анализа. Эта команда способна показать подробности, извлечённые из «магических» чисел и заголовков.

Заключение

Файлы ELF предназначены для исполнения и линковки. В зависимости от назначения, они содержат необходимые сегменты и секции. Ядро ОС просматривает сегменты и отображает их в память (используя mmap). Секции просматриваются линкером, который создаёт исполняемый файл или разделяемый объект.

Файлы ELF очень гибкие и поддерживаются различные типы CPU, машинные архитектуры, и операционные системы. Также он расширяемый, каждый файл сконструирован по-разному, в зависимости от требуемых частей. Путём использования правильных инструментов, вы сможете разобраться с назначением файла, и изучать содержимое бинарных файлов. Можно просмотреть функции и строки, содержащиеся в файле. Хорошее начало для тех, кто исследует вредоносные программы, или понять, почему процесс ведёт себя (или не ведёт) определённым образом.

Ресурсы для дальнейшего изучения

Если вы хотите больше знать про ELF и обратную разработку, вы можете посмотреть работу, которую мы выполняем в Linux Security Expert. Как часть учебной программы, мы имеем модуль обратной разработки с практическими лабораторными работами.

Для тех из вас, кто любит читать, хороший и глубокий документ: ELF Format и документ за авторством Брайана Рейтера (Brian Raiter), также известного как ELFkickers. Для тех, кто любит разбираться в исходниках, посмотрите на документированный заголовок ELF от Apple.

Совет:
если вы хотите стать лучше в анализе файлов, начните использовать популярные инструменты анализа, которые доступны в настоящее время.


Что такое compile-time алгоритмы и для чего они нужны?
А есть от них хоть какая-то практическая польза? По-моему нет


Для чего нужны сужающие преобразования как они работают и на сколько они важны?
Я читаю одну книгу и застрял на одной теме "Преобразования", и там есть такой вот код double х.


Allocator_traits propagate_on_container_ - для чего они нужны
В std::allocator_traits есть такие определения как propagate_on_container_copy_assignment.


.clear() и .seekg() Для чего они нужны?
Здравствуйте! Изучал код и увидел 2 интересные строки, начал гуглить, но внятного ответа не нашел.

Для чего нужны? Для того же, для чего и exe под виндой - бинарный формат для загрузки в память и запуска. Форматы и первого и второго описаны и доступны. Разница везде. Для чего нужны? Для того же, для чего и exe под виндой - бинарный формат для загрузки в память и запуска.

так в линуксе не нужно .exe а приложение запускается
и я не разу не видел чтобы при дабл клике на elf запускалась программа

можете описать применение такого файла, на примере

В линуксе бинарные исполняемые файлы (и библиотеки тоже) - elf. В линуксе бинарные исполняемые файлы (и библиотеки тоже) - elf. elf но без .elf в названии исполняемого файла посколку линуксу это не нужно
правильно? так в линуксе не нужно .exe а приложение запускается Что это за бессмысленная фраза? Да, в линуксе программы запускаются. Сам в шоке. и я не разу не видел чтобы при дабл клике на elf запускалась программа Это зависит от файлового менеджера, а не формата файла.

Запусти /bin/bash - применение. Запустил командный интерпретатор.

Добавлено через 44 секунды

elf но без .elf в названии исполняемого файла посколку линуксу это не нужно
правильно? Не нужно. Можешь .doc поставить. ничего не изменится. Если не считать файловые менеджеров и их особенности. В линуксе бинарные исполняемые файлы (и библиотеки тоже) - elf. elf но без .elf в названии исполняемого файла посколку линуксу это не нужно
правильно? Запусти /bin/bash - применение. Запустил командный интерпретатор. я вас вообще не понимаю
причем тут скрипты к бинарникам?

А надо было взять и почитать книгу

Добавлено через 23 секунды

При том, что /bin/bash - это elf-файл. Скрипты здесь не при чем. При том, что /bin/bash - это elf-файл. Скрипты здесь не при чем.

а, понял - сам интерпретатор

окю тогда так. я компилирую код с gcc на линуксе
какой у финального файла будет формат?

elf но без .elf в названии исполняемого файла посколку линуксу это не нужно
правильно?

Расширение не нужно, потому что исполняемость файла определяется правами в файловой системе.

$ objdump -f /usr/bin/bash

/usr/bin/bash: формат файла elf32-i386
архитектура: i386, флаги 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
начальный адрес 0x08060238

запусти команду file на результат работы компилятора и она тебе напишет, что за файл. так в линуксе не нужно .exe а приложение запускается
и я не разу не видел чтобы при дабл клике на elf запускалась программа

ELF - это формат файла, в который по специальным правилам записаны коды, данные, всякая информация для отладчика или дизассемблера и прочее барахло

Читайте также: