How to open elf file on linux
How to open elf file on linux
Understanding the ELF File Format
From Source Code To Binary Code
Programming starts with having a clever idea, and writing source code in a programming language of your choice, for example C, and saving the source code in a file. With the help of an adequate compiler, for example GCC, your source code is translated into object code, first. Eventually, the linker translates the object code into a binary file that links the object code with the referenced libraries. This file contains the single instructions as machine code that are understood by the CPU, and are executed as soon the compiled program is run.
The binary file mentioned above follows a specific structure, and one of the most common ones is named ELF that abbreviates Executable and Linkable Format. It is widely used for executable files, relocatable object files, shared libraries, and core dumps.
Twenty years ago – in 1999 – the 86open project has chosen ELF as the standard binary file format for Unix and Unix-like systems on x86 processors. Luckily, the ELF format had been previously documented in both the System V Application Binary Interface, and the Tool Interface Standard [4]. This fact enormously simplified the agreement on standardization between the different vendors and developers of Unix-based operating systems.
The reason behind that decision was the design of ELF – flexibility, extensibility, and cross-platform support for different endian formats and address sizes. ELF’s design is not limited to a specific processor, instruction set, or hardware architecture. For a detailed comparison of executable file formats, have a look here [3].
Since then, the ELF format is in use by several different operating systems. Among others, this includes Linux, Solaris/Illumos, Free-, Net- and OpenBSD, QNX, BeOS/Haiku, and Fuchsia OS [2]. Furthermore, you will find it on mobile devices running Android, Maemo or Meego OS/Sailfish OS as well as on game consoles like the PlayStation Portable, Dreamcast, and Wii.
The Structure of an ELF File
On a Linux terminal, the command man elf gives you a handy summary about the structure of an ELF file:
Listing 1: The manpage of the ELF structure
ELF(5) Linux Programmer’s Manual ELF(5)
DESCRIPTION
The header file defines the format of ELF executable binary
files. Amongst these files are normal executable files, relocatable
object files, core files and shared libraries.
An executable file using the ELF file format consists of an ELF header,
followed by a program header table or a section header table, or both.
The ELF header is always at offset zero of the file. The program
header table and the section header table’s offset in the file are
defined in the ELF header. The two tables describe the rest of the
particularities of the file.
As you can see from the description above, an ELF file consists of two sections – an ELF header, and file data. The file data section can consist of a program header table describing zero or more segments, a section header table describing zero or more sections, that is followed by data referred to by entries from the program header table, and the section header table. Each segment contains information that is necessary for run-time execution of the file, while sections contain important data for linking and relocation. Figure 1 illustrates this schematically.
The ELF Header
The ELF header is 32 bytes long, and identifies the format of the file. It starts with a sequence of four unique bytes that are 0x7F followed by 0x45, 0x4c, and 0x46 which translates into the three letters E, L, and F. Among other values, the header also indicates whether it is an ELF file for 32 or 64-bit format, uses little or big endianness, shows the ELF version as well as for which operating system the file was compiled for in order to interoperate with the right application binary interface (ABI) and cpu instruction set.
The hexdump of the binary file touch looks as follows:
.Listing 2: The hexdump of the binary file
.Listing 3: Displaying the header of an ELF file
The Program Header
The program header shows the segments used at run-time, and tells the system how to create a process image. The header from Listing 2 shows that the ELF file consists of 9 program headers that have a size of 56 bytes each, and the first header starts at byte 64.
.Listing 4: Display information about the program headers
Elf file type is EXEC (Executable file)
Entry point 0x4025e3
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000d494 0x000000000000d494 R E 200000
LOAD 0x000000000000de10 0x000000000060de10 0x000000000060de10
0x0000000000000524 0x0000000000000748 RW 200000
DYNAMIC 0x000000000000de28 0x000000000060de28 0x000000000060de28
0x00000000000001d0 0x00000000000001d0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000000bc40 0x000000000040bc40 0x000000000040bc40
0x00000000000003a4 0x00000000000003a4 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x000000000000de10 0x000000000060de10 0x000000000060de10
0x00000000000001f0 0x00000000000001f0 R 1
The Section Header
.Listing 5: Section details revealed by readelf
Tools to Analyze an ELF file
As you may have noted from the examples above, GNU/Linux is fleshed out with a number of useful tools that help you to analyze an ELF file. The first candidate we will have a look at is the file utility.
file displays basic information about ELF files, including the instruction set architecture for which the code in a relocatable, executable, or shared object file is intended. In listing 6 it tells you that /bin/touch is a 64-bit executable file following the Linux Standard Base (LSB), dynamically linked, and built for the GNU/Linux kernel version 2.6.32.
.Listing 6: Basic information using file
.Listing 7: Display Selected sections of an ELF file
Displaying notes found at file offset 0x00000254 with length 0x00000020:
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 2.6.32
Displaying notes found at file offset 0x00000274 with length 0x00000024:
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: ec08d609e9e8e73d4be6134541a472ad0ea34502
Note that under Solaris and FreeBSD, the utility elfdump [7] corresponds with readelf. As of 2019, there has not been a new release or update since 2003.
Number three is the package named elfutils [6] that is purely available for Linux. It provides alternative tools to GNU Binutils, and also allows validating ELF files. Note that all the names of the utilities provided in the package start with eu for ‘elf utils’.
Last but not least we will mention objdump. This tool is similar to readelf but focuses on object files. It provides a similar range of information about ELF files and other object formats.
.Listing 8: File information extracted by objdump
/bin/touch: file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000004025e3
There is also a software package called ‘elfkickers’ [9] which contains tools to read the contents of an ELF file as well as manipulating it. Unfortunately, the number of releases is rather low, and that’s why we just mention it, and do not show further examples.
As a developer you may have a look at ‘pax-utils’ [10,11], instead. This set of utilities provides a number of tools that help to validate ELF files. As an example, dumpelf analyzes the ELF file, and returns a C header file containing the details – see Figure 2.
Conclusion
Thanks to a combination of clever design and excellent documentation the ELF format works very well, and is still in use after 20 years. The utilities shown above allow you an insight view into an ELF file, and let you figure out what a program is doing. These are the first steps for analyzing software – happy hacking!
Links and References
Acknowledgements
The writer would like to thank Axel Beckert for his support regarding the preparation of this article.
Введение в ELF-файлы в Linux: понимание и анализ
Есть в мире вещи, которые мы принимаем как нечто само собой разумеющееся, хотя они являются истинными шедеврами. Одними из таких вещей являются утилиты Linux, такие, как ls и ps. Хотя они обычно воспринимаются как простые, это оказывается далеко не так, если мы заглянем внутрь. И таким же оказывается ELF, Executable and Linkable Format. Формат файлов, который используется повсеместно, но мало кто его понимает. Это краткое руководство поможет вам достичь понимания.
Прочтя это руководство, вы изучите:
Что представляет собой файл ELF?
ELF — это сокращение от Executable and Linkable Format (формат исполняемых и связываемых файлов) и определяет структуру бинарных файлов, библиотек, и файлов ядра (core files). Спецификация формата позволяет операционной системе корректно интерпретировать содержащиеся в файле машинные команды. Файл ELF, как правило, является выходным файлом компилятора или линкера и имеет двоичный формат. С помощью подходящих инструментов он может быть проанализирован и изучен.
Зачем изучать ELF в подробностях?
Перед тем, как погрузиться в технические детали, будет не лишним объяснить, почему понимание формата ELF полезно. Во-первых, это позволяет изучить внутреннюю работу операционной системы. Когда что-то пошло не так, эти знания помогут лучше понять, что именно случилось, и по какой причине. Также возможность изучения ELF-файлов может быть ценна для поиска дыр в безопасности и обнаружения подозрительных файлов. И наконец, для лучшего понимания процесса разработки. Даже если вы программируете на высокоуровневом языке типа Go, выа всё равно будет лучше знать, что происходит за сценой.
Итак, зачем изучать ELF?
От исходника к процессу
Какую бы операционную систему мы не использовали, необходимо каким-то образом транслировать функции исходного кода на язык 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 и все значения.
Выделенное поле определяет тип машины. Значение 3e — это десятичное 62, что соответствует AMD64. Чтобы получить представление обо всех типах файлов, посмотрите этот заголовочный файл.
Хотя вы можете делать всё это в шестнадцатиричном дампе, имеет смысл использовать инструмент, который сделает работу за вас. Утилита dumpelf может быть полезна. Она показывает форматированный вывод, соответствующий заголовку ELF. Хорошо будет изучить, какие поля используются, и каковы их типичные значения.
Теперь, кгда мы объяснили значения этих полей, время посмотреть на то, какая реальная магия за ними стоит, и перейти к следующим заголовкам!
Данные файла
Помимо заголовка, файлы ELF состоят из трёх частей.
Заголовки программы
Файл ELF состоит из нуля или более сегментов, и описывает, как создать процесс, образ памяти для исполнения в рантайме. Когда ядро видит эти сегменты, оно размещает их в виртуальном адресном пространстве, используя системный вызов mmap(2). Другими словами, конвертирует заранее подготовленные инструкции в образ в памяти. Если ELF-файл является обычным бинарником, он требует эти программные заголовки, иначе он просто не будет работать. Эти заголовки используются, вместе с соответствующими структурами данных, для формирования процесса. Для разделяемых библиотек (shared libraries) процесс похож.
Программный заголовок в бинарном ELF-файле
Мы видим в этом примере 9 программных заголовков. Сначала трудно понять, что они означают. Давайте погрузимся в подробности.
GNU_EH_FRAME
Это сортированная очередь, используемая компилятором GCC. В ней хранятся обработчики исключений. Если что-то пошло не так, они используются для того, чтобы корректно обработать ситуацию.
GNU_STACK
Этот заголовок используется для сохранения информации о стеке. Интересная особенность состоит в том, что стек не должен быть исполняемым, так как это может повлечь за собой уязвимости безопасности.
Если сегмент GNU_STACK отсутствует, используется исполняемый стек. Утилиты scanelf и execstack показывают детали устройства стека.
Секции ELF
Заголовки секции
Заголовки секции определяют все секции файла. Как уже было сказано, эта информация используется для линковки и релокации.
Секции появляются в ELF-файле после того, как компилятор GNU C преобразует код С в ассемблер, и ассемблер GNU создаёт объекты.
Содержит исполняемый код. Он будет упакован в сегмент с правами на чтение и на исполнение. Он загружается один раз, и его содержание не изменяется. Это можно увидеть с помощью утилиты objdump.
Инициализированные данные, с правами на чтение и запись.
.rodata
Инициализированные данные, с правами только на чтение. (=A).
Неинициализированные данные, с правами на чтение/запись. (=WA)
Группы секций
Некоторые секции могут быть сгруппированы, как если бы они формировали единое целое. Новые линкеры поддерживают такую функциональность. Но пока такое встречается не часто.
Хотя это может показаться не слишком интересным, большие преимущества даёт знание инструментов анализа 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.
Совет:
если вы хотите стать лучше в анализе файлов, начните использовать популярные инструменты анализа, которые доступны в настоящее время.
Рецепты для ELFов
На русском языке довольно мало информации про то, как работать с ELF-файлами (Executable and Linkable Format — основной формат исполняемых файлов Linux и многих Unix-систем). Не претендуем на полное покрытие всех возможных сценариев работы с эльфами, но надеемся, что информация будет полезна в виде справочника и сборника рецептов для программистов и реверс-инженеров.
Подразумевается, что читатель на базовом уровне знаком с форматом ELF (в противном случае рекомендуем цикл статей Executable and Linkable Format 101).
Под катом будут перечислены инструменты для работы, описаны приемы для чтения метаинформации, модификации, проверки и размножения создания эльфов, а также приведены ссылки на полезные материалы.
— Я тоже эльф… Синий в красный… Эльфы очень терпеливы… Синий в красный… А мы эльфы. Синий в красный… От магии одни беды…
(с) Маленькое королевство Бена и Холли
Инструменты
В большинстве случаев примеры можно выполнить как на Linux, так и на Windows.
В рецептах мы будем использовать следующие инструменты:
Тестовые эльфы
В качестве «подопытного» будем использовать ELF-файл simple из таска nutcake’s PieIsMyFav на crackmes.one, но подойдёт любой представитель «эльфийского» семейства. Если готовый файл с требуемыми характеристиками не был найден в свободном доступе, то будет приведён способ создания такого эльфа.
«Свободных» эльфов можно также найти по ссылкам:
Чтение, получение информации
Тип файла, заголовок, секции
В зависимости от задачи интерес могут представлять:
010Editor
HEX-редактор 010Editor предоставляет систему шаблонов. Для ELF-файлов шаблон называется, как ни странно, ELF.bt и находится в категории Executable (меню Templates — Executable).
Интерес может представлять, например, точка входа в исполняемый файл (entry point) (записана в заголовке файла).
readelf
Утилиту readelf можно считать стандартом де-факто для получения сведений об ELF-файле.
Для удобства чтения адреса приведены к 32-битному формату:
Для удобства чтения адреса приведены к 32-битному формату:
Вывод сокращён для удобства чтения:
Прочитать заголовок и информацию о секциях можно с использованием кода на Python и библиотеки LIEF (предоставляет API не только для Python):
Информация о компиляторе
objdump
readelf
Я вычислю тебя по… RPATH
Эльфы могут сохранять пути для поиска динамически подключаемых библиотек. Чтобы не задавать системную переменную LD_LIBRARY_PATH перед запуском приложения, можно просто «вшить» этот путь в ELF-файл.
И будь осторожен, юный разработчик, не «спали» свою директорию проекта!
Как появляется RPATH?
readelf
Для удобства чтения результат команды сокращён:
С помощью библиотеки LIEF также можно прочитать RPATH-запись в эльфе:
Проверка эльфа на безопасность
Скрипт проверки безопасности checksec.sh от исследователя Tobias Klein (автора книги A Bug Hunter’s Diary) не обновлялся с 2011 года. Данный скрипт для ELF-файлов выполняет проверку наличия опций RelRO (Read Only Relocations), NX (Non-Executable Stack), Stack Canaries, PIE (Position Independent Executables) и для своей работы использует утилиту readelf.
Можно сделать свой аналог на коленке Python и LIEF (чуть короче прародителя и с дополнительной проверкой опции separate-code):
Radare2
Спасибо dukebarman за дополнение по использованию Radare2 для вывода информации аналогично checksec:
«Сырой код» из эльфа (binary from ELF)
Бывают ситуации, когда «эльфийские одёжи» в виде ELF-структуры не нужны, а нужен только «голый» исполняемый код приложения.
objcopy
Использование objcopy вероятно знакомо тем, кто пишет прошивки:
Никакой магии. Просто взять содержимое загружаемых секций и слепить из них бинарь:
Mangled — demangled имена функций
В ELF-ах, созданных из С++ кода, имена функций декорированы (манглированы) для упрощения поиска соответствующей функции класса. Однако читать такие имена при анализе не очень удобно.
Для представления имён в удобочитаемом виде можно использовать утилиту nm из набора binutils:
Вывод имён символов в деманглированном виде с использованием библиотеки LIEF:
Сборка, запись, модификация эльфа
Эльф без метаинформации
После того как приложение отлажено и выпускается в дикий мир, имеет смысл удалить метаинформацию:
Удаление символьной информации
Символьная информация — это имена объектов и функций. Без неё реверс приложения немного усложняется.
strip
В самом простом случае можно воспользоваться утилитой strip из набора binutils. Для удаления всей символьной информации достаточно выполнить команду:
sstrip
Для тщательного удаления символьной информации (в том числе ненужных нулевых байтов в конце файла) можно воспользоваться утилитой sstrip из набора ELFkickers. Для удаления всей символьной информации достаточно выполнить команду:
Удаление таблицы секций
Как упоминалось выше, наличие/отсутствие таблицы секций не оказывает влияния на работу приложения. Но при этом без таблицы секций реверс приложения становится чуть сложнее.
Воспользуемся библиотекой LIEF под Python и примером удаления таблицы секций:
Изменение и удаление RPATH
chrpath, PatchELF
Для изменения RPATH под Linux можно воспользоваться утилитами chrpath (доступна в большинстве дистрибутивов) или PatchELF.
ELF Loaders, Libraries and Executables on Linux
The target of the following stream of ASCII characters is to shed some light on an area that is probably not common knowledge even for experienced system developers: loaders, libraries and executables in the ELF Linux ecosystem.
First, we’ll try to understand what happens when we run a basic program on our Linux machine. Then we’ll discuss libraries and what they bring to the table. Cover the differences between static and dynamic libraries and executables, and finally dive into the inner-works of the dynamic loader, learning how to control, configure and manipulate it.
Hopefully, we can cover enough concepts, tools and approaches to ultimately provide you with an efficient tool-set for tackling related issues when managing, deploying or developing applications for Linux systems.
Here are the subjects we’ll attempt to recount:
Behind the scenes of “Hello World”
I want to believe that everybody have seen the following piece of C code at least once in their lives:
And then we’ve probably compiled and executed it:
But, did we really understand what happened there during these two trivial steps we went through? What is this myapp file that got created? How could our program use the printf method without implementing it? And what happened when we executed it?
We’ll try to address each of these questions in the same order in the following paragraphs.
Executable and Linkable Format (ELF)
People don’t really realize that, but the ELF file format is a cornerstone for all their magical applications! ELF is a standard file format for executables, libraries, and more.
Just like in our “Hello World” application; myapp is an ELF file (without even knowing it) and used other modules, such as the dynamic loader (which we’ll talk about later) to execute it.
By design, the ELF format is flexible, extensible, and cross-platform. It supports different endianness and address sizes so it can be used on any CPU or instruction set architecture. This has promoted its adoption by many different operating systems, and made it the de facto standard for Linux and other Unix-like systems.
Since the ELF format is a topic on its own, we won’t dwell into that, but if you want some more information, take a look at this comprehensive ELF standard.
Who the hell needs libraries?
One of the most important concepts in Software Engineering is code reuse. There are numerous reasons for not writing the same code more than once, or using different instances of the same code snippet.
This is just like in our “Hello World” application, where we used the printf method without implementing it. Because printing to the screen is required by so many applications out there, it was implemented once, and is now readily available for anyone writing C or C++ applications.
In order to implement this, libraries were invented. Libraries allow programmers to pack code into reusable modules. Then, every program that requires the same functionality can simply use the already-existing library. There are actually two kinds of libraries:
Static libraries (.a files): Libraries that become part of the executable (are actually embedded into it) during linking. Which means that once the executable is ready, it doesn’t require any additional files.
Dynamic libraries (.so files): Libraries that are shipped separately from the executable, and are required to be present at run-time. We can use these libraries in two different ways, and the difference is mainly whether or not the libraries are present during compile-time:
Libraries needed by an executable
Now that we know what libraries are, we’ll define two new terms:
For example, when we use it on our “Hello World” application, we see that it is dynamically linked (emphasis added), thus requiring dynamic libraries:
> Note: Static executables are ‘statically linked’
But, how is this possible? We haven’t told anyone anything about additional libraries! Why is it dynamically linked?
Well, it turns out that by default, when we compile a C/C++ application, the compiler is programmed to automatically use the standard libraries: glibc.so for C and additionally libstdc++.so for C++.
Now that we know our application is a dynamic executable, how can we tell which libraries it requires?
Static enumeration of needed libraries
For example, if we use it on our “Hello World” application, we get the following output:
command, and look for the keyword NEEDED :
Dynamic enumeration of needed libraries
But, how can we check what library the loader is actually going to load? libc.so.6 can exist at multiple locations, which one are we actually going to use?
In order to collect additional information, we can use a great script called ldd (List Dynamic Dependencies). This script enumerates all the dynamic libraries required by a specific executable. And, unlike the static enumeration methods, this tool describes the full path(!) of the library we are going to use. For example, the output for our “Hello World” application is:
As you can see, ldd has a much richer output. Right now, we’ll ignore the first and last line, and focus on the line in bold, that follows the format:
Well, I’ve been using the ‘loader’ term intermittently in the last few paragraphs, but this is the first time we have actually seen it in action. This is probably a good time to entertain ourselves with some more interesting facts about it.
Loader who?
As you could guess by now, the dynamic loader is a single static executable ELF file that goes by a name usually reserved by libraries.
The weird thing about it, is that it can be run either indirectly, by running some dynamically linked program or shared object, or directly, just like we’ve seen in the ldd example.
Use your own loader
Well, must we use the system’s loader? Can you BYOL? On the one hand, this is something every systems programmer wishes to avoid. The different package managers take care of many compatibility issues, and once you step off that train and into the wild, a lot of things can go wrong at every turn you take.
On the other hand, there is a method of distributing software as an encapsulated bundle that depends on it. When following this convention, applications are compiled against and shipped with a very specific set of libraries (I consider the loader to be one of them), thus creating a package that is (almost) independent of the hosting system. To accomplish this goal, we have to “patch” our executable and tell it to use our own loader.
To find out which loader an ELF “expects”, we can use the file app (emphasis added):
Whereas to change the loader for a specific ELF, we can use patchelf and specify the interpreter and the elf to patch:
But, when shipping such a specially crafted package, one also has to make sure that loader loads the libraries he shipped with it, and not the ones pre-installed on the target system. To accomplish that, we have to understand how the loader works, and how it decided which libraries to load.
What does the loader load?
When we load a static executable, there is no need for the dynamic loader, because all the required libraries were integrated into the executable during link-time.
However, when we load a dynamic executable, the system depends on the loader to do the heavy lifting.
The loader always starts by loading:
If one of these libraries requires an additional library, not required by the executable, this library is referred to as an ‘indirect dependency’. And once we finished loading all the direct dependencies, the loader loads the indirect ones.
For each (direct/indirect) dependency, the loader follows this decision making process:
First, if the dependency string contains a slash, e.g. /mylib (can occur if one was specified during link time), it is interpreted as a (relative or absolute) pathname and loaded from there and no further lookup is required.
Otherwise, the loader follows these steps (Each step contains a condition. If that condition is not satisfied, the loader will skip it):
Step 1: Using the directories in DT_RPATH
Step 2: Using the environment variable LD_LIBRARY_PATH
Step 3: Using the directories in DT_RUNPATH
Step 4: From the cache file /etc/ld.so.cache
Do you feel like five different preconditioned steps is enough? Well, lucky for us, that’s it for now. If the loader goes through all five steps and still it cannot find a library, the load process fails, and a similar error message is printed:
Now that we know what guides the loader while trying to do its job, let’s take it to the next step, and see how we can advise. To do that, we’ll recap and elaborate on some of the UPPERCASE keywords we’ve just seen.
R[UN]PATH
As we already understand, their goal is to empower the developer and allow him to alter the behavior of the dynamic loader.
In case you didn’t read the last chapter, or just to sharpen your grasp of these values, let’s recap:
Having a good understanding of the consequences, we can now start talking about the next phase, how can we manipulate it?
First, before we start breaking things, we should be able to know what we’re dealing with. Given an ELF file, we can examine R[UN]PATH values using:
When RPATH and RUNPATH are not set, there won’t be any output, otherwise, we’ll see the dynamic section entry:
Now, for the fun part. We can manipulate an ELF in two very different ways:
NODEFLIB
> Note: This request holds, even if it means that the load operation will fail.
How can we tell if this flag is set? Well, once more, we print out the dynamic sections of the ELF and look for the NODEFLIB expression:
And how can we manipulate it? Similarly to R[UN]PATH:
This is the end, and I hope you have enjoyed scrolling down to the bottom of this article 🙂
Now seriously, I hope that after reading this, you feel better equipped for fighting those sorts of issues on your system. If you think that this article could have used an additional chapter, please let me know!
If you feel that you’ve gained some insights from reading this, I would really appreciate it if you clap!
Введение в ELF-файлы в Linux: понимание и анализ
Есть в мире вещи, которые мы принимаем как нечто само собой разумеющееся, хотя они являются истинными шедеврами. Одними из таких вещей являются утилиты Linux, такие, как ls и ps. Хотя они обычно воспринимаются как простые, это оказывается далеко не так, если мы заглянем внутрь. И таким же оказывается ELF, Executable and Linkable Format. Формат файлов, который используется повсеместно, но мало кто его понимает. Это краткое руководство поможет вам достичь понимания.
Прочтя это руководство, вы изучите:
Что представляет собой файл ELF?
ELF — это сокращение от Executable and Linkable Format (формат исполняемых и связываемых файлов) и определяет структуру бинарных файлов, библиотек, и файлов ядра (core files). Спецификация формата позволяет операционной системе корректно интерпретировать содержащиеся в файле машинные команды. Файл ELF, как правило, является выходным файлом компилятора или линкера и имеет двоичный формат. С помощью подходящих инструментов он может быть проанализирован и изучен.
Зачем изучать ELF в подробностях?
Перед тем, как погрузиться в технические детали, будет не лишним объяснить, почему понимание формата ELF полезно. Во-первых, это позволяет изучить внутреннюю работу операционной системы. Когда что-то пошло не так, эти знания помогут лучше понять, что именно случилось, и по какой причине. Также возможность изучения ELF-файлов может быть ценна для поиска дыр в безопасности и обнаружения подозрительных файлов. И наконец, для лучшего понимания процесса разработки. Даже если вы программируете на высокоуровневом языке типа Go, вы всё равно будет лучше знать, что происходит за сценой.
Итак, зачем изучать ELF?
От исходника к процессу
Какую бы операционную систему мы не использовали, необходимо каким-то образом транслировать функции исходного кода на язык 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 и все значения.
Выделенное поле определяет тип машины. Значение 3e — это десятичное 62, что соответствует AMD64. Чтобы получить представление обо всех типах файлов, посмотрите этот заголовочный файл.
Хотя вы можете делать всё это в шестнадцатиричном дампе, имеет смысл использовать инструмент, который сделает работу за вас. Утилита dumpelf может быть полезна. Она показывает форматированный вывод, соответствующий заголовку ELF. Хорошо будет изучить, какие поля используются, и каковы их типичные значения.
Теперь, кгда мы объяснили значения этих полей, время посмотреть на то, какая реальная магия за ними стоит, и перейти к следующим заголовкам!
Данные файла
Помимо заголовка, файлы ELF состоят из трёх частей.
Заголовки программы
Файл ELF состоит из нуля или более сегментов, и описывает, как создать процесс, образ памяти для исполнения в рантайме. Когда ядро видит эти сегменты, оно размещает их в виртуальном адресном пространстве, используя системный вызов mmap(2). Другими словами, конвертирует заранее подготовленные инструкции в образ в памяти. Если ELF-файл является обычным бинарником, он требует эти программные заголовки, иначе он просто не будет работать. Эти заголовки используются, вместе с соответствующими структурами данных, для формирования процесса. Для разделяемых библиотек (shared libraries) процесс похож.
Программный заголовок в бинарном ELF-файле
Мы видим в этом примере 9 программных заголовков. Сначала трудно понять, что они означают. Давайте погрузимся в подробности.
GNU_EH_FRAME
Это сортированная очередь, используемая компилятором GCC. В ней хранятся обработчики исключений. Если что-то пошло не так, они используются для того, чтобы корректно обработать ситуацию.
GNU_STACK
Этот заголовок используется для сохранения информации о стеке. Интересная особенность состоит в том, что стек не должен быть исполняемым, так как это может повлечь за собой уязвимости безопасности.
Если сегмент GNU_STACK отсутствует, используется исполняемый стек. Утилиты scanelf и execstack показывают детали устройства стека.
Команды для просмотра программного заголовка:
Секции ELF
Заголовки секции
Заголовки секции определяют все секции файла. Как уже было сказано, эта информация используется для линковки и релокации.
Секции появляются в ELF-файле после того, как компилятор GNU C преобразует код С в ассемблер, и ассемблер GNU создаёт объекты.
Содержит исполняемый код. Он будет упакован в сегмент с правами на чтение и на исполнение. Он загружается один раз, и его содержание не изменяется. Это можно увидеть с помощью утилиты objdump.
Инициализированные данные, с правами на чтение и запись.
.rodata
Инициализированные данные, с правами только на чтение. (=A).
Неинициализированные данные, с правами на чтение/запись. (=WA)
Команды для просмотра секций и заголовков.
Группы секций
Некоторые секции могут быть сгруппированы, как если бы они формировали единое целое. Новые линкеры поддерживают такую функциональность. Но пока такое встречается не часто.
Хотя это может показаться не слишком интересным, большие преимущества даёт знание инструментов анализа 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.
Совет:
если вы хотите стать лучше в анализе файлов, начните использовать популярные инструменты анализа, которые доступны в настоящее время.