Ld linux so 2 что это

Обновлено: 04.07.2024

Для двоичных файлов Linux требуется динамическая компоновка (компоновка во время выполнения), если при сборке программе ld(1) не был передан параметр -static.

Программа ld.so предназначена для обработки двоичных файлов в формате a.out (старый формат); ld-linux.so* предназначена для обработки файлов в формате ELF (/lib/ld-linux.so.1, если используется libc5 и /lib/ld-linux.so.2, если glibc2), который используется последние несколько лет. Обе программы ведут себя одинаково и используют те же самые файлы поддержки и программы ldd(1), ldconfig(8) и /etc/ld.so.conf.

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

Если в зависимости общего объекта отсутствует косая черта, то поиск выполняется в следующем порядке:

Раскрытие токена rpath

ld.so распознаёт определённые строки согласно спецификации rpath (DT_RPATH или DT_RUNPATH); эти строки заменяются на

$ORIGIN (или $ ) Она раскрывается в каталог, содержащий программу или общий объект. Таким образом, приложение, расположенное в somedir/app может компилироваться с

ПАРАМЕТРЫ

--list Выдать список всех зависимостей и как они удовлетворяются.

--verify Проверить, что программа является динамически скомпонованной и что компоновщик понимает её формат.

--inhibit-cache Не использовать /etc/ld.so.cache.

--library-path путь Использовать путь вместо значения переменной окружения LD_LIBRARY_PATH (смотрите далее).

--inhibit-rpath список Игнорировать информацию в RPATH и RUNPATH об именах объектов из списка. Этот параметр игнорируется в режиме безопасного выполнения (смотрите далее).

--audit список Использовать объекты из списка в качестве аудиторов.

ОКРУЖЕНИЕ

На поведение динамического компоновщика влияют различные переменные окружения.

Безопасный режим выполнения

Переменные окружения

Среди наиболее важных переменных окружения следует выделить следующие:

LD_ASSUME_KERNEL можно использовать, чтобы заставить динамический компоновщик предполагать, что он работает в системе с другой версией ядра ABI. Например, следующая команда заставляет динамический компоновщик при загрузке общих объектов, требуемых myprog, предполагать, что он запущен на Linux с версией 2.2.5:

В системах, предоставляющих несколько версий общего объекта (в различных каталогах пути поиска) с разными требованиями к минимальной версии ядра ABI, LD_ASSUME_KERNEL может использоваться для выбора версии объекта, которую нужно задействовать (в зависимости от порядка поиска в каталогах). Исторически, свойство LD_ASSUME_KERNEL наиболее часто использовалось при ручном выборе старых реализаций нитей POSIX LinuxThreads в системах, которые предоставляли и LinuxThreads, и NPTL (в последствии стала базовой в таких системах); смотрите pthreads(7).

LD_BIND_NOW (libc5; в glibc начиная с версии 2.1.1) Если переменная содержит непустую строку, то динамический компоновщик будет искать все символы при запуске программы вместо того, чтобы отложить поиск вызовов функций до момента, когда они встретятся в первый раз. Это полезно при отладке.

LD_LIBRARY_PATH Список каталогов, в которых ищутся библиотеки ELF при выполнении. Элементы списка разделяются двоеточиями или точками с запятой. Похожа на переменную окружения PATH. Данная переменная игнорируется в режиме безопасного выполнения.

LD_PRELOAD Список дополнительных, указанных пользователем, общих объектов ELF для загрузки раньше чем все остальные. Элементы списка разделяются пробелами или двоеточиями. Может использоваться для выборочной замены функций из других общих объектов. Объекты ищутся согласно правилам, указанным в ОПИСАНИИ. В режиме безопасного выполнения предварительная загрузка файлов с символами косой черты не выполняется, а общие объекты из стандартных каталогов поиска загружаются только, если на файле общего объекта включён бит режима set-user-ID.

LD_TRACE_LOADED_OBJECTS (только для ELF) Если установлена, то вместо нормального запуска программы будут выданы её динамические зависимости, как если бы она была запущена ldd(1).

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

LD_AOUT_LIBRARY_PATH (libc5) Тоже что и LD_LIBRARY_PATH, но только для двоичных файлов в формате a.out. Старые версии ld-linux.so.1 также поддерживают LD_ELF_LIBRARY_PATH.

LD_AOUT_PRELOAD (libc5) Тоже что и LD_PRELOAD, но только для двоичных файлов в формате a.out. Старые версии ld-linux.so.1 также поддерживают LD_ELF_PRELOAD.

LD_AUDIT (в glibc начиная с 2.4) Определяемый пользователем список общих объектов ELF разделяемых двоеточиями, которые будут загружены раньше всех остальных в отдельном пространстве имён компоновщика (т. е., они не внедряются вместо обычных привязываемых символов, которые могли бы быть в этом процессе). Эти объекты можно использовать для контрольной проверки операций динамического компоновщика. LD_AUDIT игнорируется в режиме безопасного выполнения.

LD_DEBUG (в glibc начиная с 2.1) Выводить подробную отладочную информацию о динамическом компоновщике. Если значение равно all, то выдаётся вся имеющаяся отладочная информация, если значение равно help, то выдаётся справка о некоторых категориях, которые могут быть указаны в этой переменной окружения. Начиная с glibc 2.3.4, LD_DEBUG игнорируется в режиме безопасного выполнения, если не существует файл /etc/suid-debug (содержимое может быть любым).

LD_DYNAMIC_WEAK (в glibc начиная с 2.1.91) Если эта переменная окружения определена (с любым значением), то разрешено перезаписывать слабые символы (возвращение к старому поведению glibc). Начиная с glibc 2.3.4, LD_DYNAMIC_WEAK игнорируется в режиме безопасном выполнения.

LD_HWCAP_MASK (в glibc начиная с 2.1) Маска для совместимости с аппаратными возможностями.

LD_KEEPDIR (только для a.out)(libc5) Не игнорировать каталог для загрузки в именах a.out библиотек. Настоятельно не рекомендуется использовать этот параметр.

LD_NOWARN (только a.out)(libc5) Подавлять предупреждения о библиотеках a.out с несовместимыми младшими номерами версий.

LD_ORIGIN_PATH (в glibc начиная с 2.1) Путь, где находится двоичный файл (для не set-user-ID программ). Начиная с glibc 2.4, LD_ORIGIN_PATH игнорируется в режиме безопасного выполнения.

LD_PROFILE_OUTPUT (в glibc начиная с 2.1) Каталог, куда будет сохраняться результат работы с LD_PROFILE. Если эта переменная не определена или её значение равно пустой строке, то по умолчанию результат будет сохранён в каталог /var/tmp. Переменная LD_PROFILE_OUTPUT игнорируется в режиме безопасного выполнения.

LD_SHOW_AUXV (в glibc начиная с 2.1) Если эта переменная окружения определена (с любым значением), то разрешено передавать вспомогательный вектор из ядра (смотрите также getauxval(3)). Начиная с glibc 2.3.5, LD_SHOW_AUXV игнорируется в режиме безопасного выполнения.

LD_TRACE_PRELINKING (в glibc начиная с 2.4) Если эта переменная окружения определена (с любым значением), то выполнять трассировку объекта предварительной компоновки, чьё имя указано в этой переменной окружения (для получения списка трассируемых объектов используйте ldd(1)). Если имя объекта не распознано, то трассируется все действия предварительной компоновки (prelinking activity).

LD_USE_LOAD_BIAS По умолчанию (т. е., если переменная не определена) исполняемые и предварительно скомпонованные объекты учитывают базовые адреса общих объектов, от которых они зависят, а (предварительно не скомпонованные) перемещаемые исполняемые (PIE) и другие общие объекты не учитывают их. Если переменной LD_USE_LOAD_BIAS присвоено значение 1, то и исполняемые файлы, и PIE учитывают базовые адреса. Если значение переменной LD_USE_LOAD_BIAS равно 0, то ни исполняемые файлы ни PIE не учитывают базовые адреса. Эта переменная игнорируется в режиме безопасного выполнения.

LD_VERBOSE (в glibc начиная с версии 2.1) Если значение равно непустой строке, то выводится информация о символах программы (если также установлена переменная окружения LD_TRACE_LOADED_OBJECTS).

LD_WARN (только для ELF)(в glibc начиная с 2.1.3) Если значение равно непустой строке, то выдаются предупреждения о ненайденных символах.

LD_PREFER_MAP_32BIT_EXEC (только в x86-64, в glibc начиная с 2.23) Согласно с руководством по оптимизации ПО Intel Silvermont для 64-битных приложений, выполнение предсказания ветвления может отразится негативным образом, если цель ветви находится дальше чем 4ГБ от ветви. Если эта переменная окружения установлена (любое значение), то ld.so сначала попытается отобразить исполняемые страницы с помощью mmap(2) с флагом MAP_32BIT и, если это не удастся, то выполнит отображение без флага. Примечание: с флагом MAP_32BIT отображение выполняется в нижние 2ГБ (не 4ГБ) адресного пространства. Так как при MAP_32BIT сокращается адресный диапазон, доступный для случайного выравнивания адресного пространства (ASLR), то LD_PREFER_MAP_32BIT_EXEC всегда отключается в режиме безопасного выполнения.

LDD_ARGV0 (libc5) Значение argv[0] будет использоваться ldd(1), если другого нет.

ФАЙЛЫ


/lib/ld.so динамический компоновщик/загрузчик a.out
/lib/ld-linux.so.1
,2> динамический компоновщик/загрузчик ELF
/etc/ld.so.cache Файл с скомпилированным списком каталогов, в которых производится поиск общих объектов и сортированный список общих объектов-кандидатов.
/etc/ld.so.preload Файл со списком общих объектов ELF (через пробел), которые будут загружены перед программой.

lib*.so* общие объекты

ЗАМЕЧАНИЯ

Функциональность ld.so доступна для исполняемых программ, скомпилированных с использованием libc версии 4.4.3 или выше. Функциональность ELF доступна начиная с Linux 1.1.52 и libc5.

Аппаратные возможности

Некоторые общие объекты скомпилированы с использованием специальных аппаратных инструкций, которые существуют не в каждом ЦП. Такие объекты должны быть установлены в каталоги, чью имена (например, /usr/lib/sse2/) определяют требования к аппаратным возможностям. Динамический компоновщик проверяет эти каталоги учитывая аппаратуру машины и выбирает наиболее подходящую версию требуемого общего объекта. Каталоги аппаратных возможностей могут каскадироваться для объединения свойств ЦП. Список имён поддерживаемых аппаратных возможностей зависит от ЦП. В настоящее время распознаются следующие имена:

Alpha ev4, ev5, ev56, ev6, ev67

MIPS loongson2e, loongson2f, octeon, octeon2

PowerPC 4xxmac, altivec, arch_2_05, arch_2_06, booke, cellbe, dfp, efpdouble, efpsingle, fpu, ic_snoop, mmu, notb, pa6t, power4, power5, power5+, power6x, ppc32, ppc601, ppc64, smt, spe, ucache, vsx

SPARC flush, muldiv, stbar, swap, ultra3, v9, v9v, v9v2

s390 dfp, eimm, esan3, etf3enh, g5, highgprs, hpage, ldisp, msa, stfle, z900, z990, z9-109, z10, zarch

x86 (только 32-битные) acpi, apic, clflush, cmov, cx8, dts, fxsr, ht, i386, i486, i586, i686, mca, mmx, mtrr, pat, pbe, pge, pn, pse36, sep, ss, sse, sse2, tm

Есть в мире вещи, которые мы принимаем как нечто само собой разумеющееся, хотя они являются истинными шедеврами. Одними из таких вещей являются утилиты 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.

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

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

Первая программа называется ldd. Она выдает на экран список динамических библиотек используемых в программе и их местоположение. В качестве параметра ей сообщается название обследуемой программы. Давайте попробуем использовать ее для нашей программы rezultdyn:

  • libc.so.6 - стандартную библиотеку функций языка C++.
  • ld-linux.so.2 - библиотеку динамической линковки программ ELF формата.
  • libfsdyn.so - нашу динамическую библиотеку функций.

Нашу библиотеку она найти не может. И правильно! Динамический линковщик ищет библиотеки только в известных ему каталогах, а каталог нашей программы ему явно не известен.

Для того, чтобы добавить нашу директорию с библиотекой в список известных директорий надо подредактировать файл /etc/ld.so.conf. Например, у меня этот файл состоит из таких строк:

Во всех этих директории хранятся всеми используемые библиотеки. В этом списке нет лишь одной директории - /lib, которая сама по себе не нуждается в описании, так как она является главной. Получается, что наша библиотека станет "заметной", если поместить ее в один их этих каталогов, либо отдельно описать в отдельном каталоге. Давайте для теста опишем, добавим строку в конец файла ld.so.conf:

У меня этот файл валяется в домашнем каталога пользователя root, у Вас он может быть в другом месте. Теперь после этого динамический линковщик будет знать где можно найти наш файл, но после изменения конфигурационного файла ld.so.conf необходимо, чтобы система перечитала настройки заново. Это делает программа ldconfig. Пробуем запустить нашу программу:

Как видите все заработало :) Если теперь Вы удалите добавленную нами строку и снова запустите ldconfig, то данные о расположении нашей библиотеки исчезнут и будет появляться таже самая ошибка.

Но описанный метод влияет на всю систему в целом и требует доступа администратора системы, т.е. root. А если Вы простой пользователь без сверх возможностей ?!

Для такого случая есть другое безболезненное решение. Это использование специальной переменной среды LD_LIBRARY_PATH, в которой перечисляются все каталоги содержащие пользовательские динамические библиотеки. Для того, чтобы установить эту переменную в командной среде bash надо набрать всего несколько команд. Для начала посмотрим есть ли у нас такая переменная среды:

У меня в ответ выводится пустая строка, означающая, что такой переменной среды нет. Устанавливается она следующим образом:

После этого программа rezultdyn будет прекрасно работать. В случае, если у Вас в системе эта переменная среды уже уставновлена, то, чтобы не испортить ее значение, надо новый каталог прибавить к старому значению. Делается это другой командой:

Если Вы обнулите эту переменную, то снова библиотека перестанет работать:

Вы также параллельно можете зайти в систему под другим пользователем или даже тем же самым, но если Вы захотите просмотреть значение LD_LIBRARY_PATH, то увидите ее прежнее значение. Это означает, что два разных пользователя Linux не могут влиять на работу друг друга, а это и есть самое главное хорошее отличие систем Unix от большинства других систем.

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

Заменим компоновщик, установленный в конце первого шага сборки Binutils, запуском следующей команды в директории binutils-build :

С этого момента все пакеты быдут собраны только с использованием библиотек из /tools/lib .

[Note]

Замечание

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

Теперь, когда установлен откорректированый компоновщик, вы можете удалить директории с исходниками и сборкой Binutils.

Следующим нашим действием будет исправление specs-файла GCC для задания расположения нашего компоновщика. Просто выполните следующую команду:

Мы рекомендуем просто скопировать и вставить вышеуказанное вместо того, чтобы просто вводить. Или, при желании, вы можете просто отредактировать specs-файл вручную, заменив встречающиеся строки “ /lib/ld-linux.so.2 ” на “ /tools/lib/ld-linux.so.2 ”

[Important]

Важно

Если вы работаете на платформе, где имя динамического компоновщика отлично от ld-linux.so.2 , замените“ ld-linux.so.2 ” на имя динамического компоновщика вашей платформы в вышеуказанных командах. При необходимости вернитесь к Разделу 5.3.

Есть возможность, что некоторые включаемые файлы из основной системы имеются внутри директорий для включаемых файлов GCC. Это могло произойти по причине того, что процесс “ fixincludes ” запускается как часть сборки GCC. Позже мы раскажем об этом подробнее в этой главе. А пока запустите следующую команду, чтобы исключить эту возможность:

[Caution]

Внимание

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

Если все в порядке, то не будет ошибок и вы увидите следующее:

Заметим, что /tools/lib становится префиксом динамического компоновщика.

Если эта надпись вообще не появилась или появилась другая, то что-то сильно не так. Вам надо исследовать и повторить все пройденые шаги, чтобы найти в чем проблема и устранить ее. Точки для возврата после этого места уже не будет. Сначала опять проведите предыдущий тест, используя gcc вместо cc . Если это работает, тогда отсутствует ссылка /tools/bin/cc . Обратитесь к Разделу 5.5, “GCC-3.4.1 - Шаг 1” и установите ссылку. Следующее, убедитесь в корректности PATH . Это можно проверить запуском команды echo $PATH и проверить, что /tools/bin является первым в списке. Если PATH ошибочен, это может означать, что вы зарегистрировались не как пользователь lfs или что что-то прошло не правильно в Разделе 4.4, “Установка переменных окружения”. С другими опцииями что-нибудь может пойти не так при исправлении в specs-файле выше. В этом случае, подправьте исправление в specs-файле , будьте осторожны при копировании и вставке команд .

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