Від вихідного коду до двійкового коду
Програмування починається з розумної ідеї та написання вихідного коду мовою програмування на ваш вибір, наприклад C, та збереження вихідного коду у файлі. За допомогою адекватного компілятора, наприклад GCC, ваш вихідний код спочатку перекладається в об'єктний код. Зрештою, компоновник перекладає об’єктний код у двійковий файл, який пов’язує об’єктний код із бібліотеками, на які посилаються. Цей файл містить окремі інструкції як машинний код, які розуміє центральний процесор і виконуються відразу після запуску скомпільованої програми.
Згаданий вище двійковий файл має певну структуру, і одним із найпоширеніших є ім'я ELF, що скорочує виконуваний та зв'язний формат. Він широко використовується для виконуваних файлів, переміщуваних об'єктних файлів, спільних бібліотек та дампів ядра.
Двадцять років тому - в 1999 році - проект 86open обрав ELF як стандартний бінарний формат файлів для Unix та Unix-подібних систем на процесорах x86. На щастя, формат ELF раніше був задокументований як у двійковому інтерфейсі програми System V, так і в стандарті інтерфейсу інструменту [4]. Цей факт значно спростив угоду про стандартизацію між різними постачальниками та розробниками операційних систем на базі Unix.
Причиною цього рішення був дизайн ELF - гнучкість, розширюваність та підтримка міжплатформенності для різних форматів ендіан та розмірів адрес. Дизайн ELF не обмежується конкретним процесором, набором команд або апаратною архітектурою. Детальне порівняння форматів виконуваних файлів див. Тут [3].
З тих пір формат ELF використовується у декількох різних операційних системах. Серед інших, це Linux, Solaris / Illumos, Free-, Net- та OpenBSD, QNX, BeOS / Haiku та Fuchsia OS [2]. Крім того, ви знайдете його на мобільних пристроях з ОС Android, Maemo або Meego OS / Sailfish OS, а також на ігрових консолях, таких як PlayStation Portable, Dreamcast та Wii.
Специфікація не пояснює розширення імені файлу ELF-файлів. У використанні різноманітні буквосполучення, такі як .axf, .смітник, .ельф, .o, .prx, .затяжка, .ко, .так, і .мод, або жоден.
Структура файлу ELF
У терміналі Linux команда man elf надає вам зручний підсумок про структуру файлу ELF:
Лістинг 1: Інструкція щодо структури ELF
$ людина ельфELF (5) Посібник програміста для Linux ELF (5)
НАЗВА
elf - формат файлів виконуваного файлу та формату зв’язування (ELF)
КОНТРОЛЬ
#включати
ОПИС
Файл заголовка
файлів. Серед цих файлів є звичайні виконувані файли, які можна перемістити
об'єктні файли, основні файли та спільні бібліотеки.
Виконуваний файл із використанням формату ELF складається з заголовка ELF,
за ними слід таблиця заголовків програми або таблиця заголовків розділу, або і те, і інше.
Заголовок ELF завжди має зміщений нуль файлу. Програма
таблиця заголовків та зміщення таблиці заголовків розділу у файлі
визначено в заголовку ELF. Дві таблиці описують решту
особливості файлу.
..
Як видно з наведеного вище опису, файл ELF складається з двох розділів - заголовка ELF та даних файлу. Розділ даних файлу може складатися з таблиці заголовків програми, що описує нуль або більше сегментів, таблиці заголовка розділу, що описує нуль або більше розділів, за якою слідують дані, на які посилаються записи з таблиці заголовків програми, і таблиці заголовків розділу. Кожен сегмент містить інформацію, необхідну для виконання файлу під час виконання, тоді як розділи містять важливі дані для зв'язування та переміщення. Рисунок 1 схематично це ілюструє.
Заголовок ELF
Заголовок ELF має довжину 32 байти і визначає формат файлу. Він починається з послідовності з чотирьох унікальних байтів, які є 0x7F, а потім 0x45, 0x4c і 0x46, що перекладається в три літери E, L і F. Серед інших значень заголовок також вказує, чи це файл ELF для 32- або 64-розрядного формату, використовує малу або велику обмеженість, показує версію ELF, а також для якої операційної системи файл скомпільований для взаємодії з правильний двійковий інтерфейс програми (ABI) та набір інструкцій процесора.
Шістнадцятковий дотик дотику двійкового файлу виглядає наступним чином:
.Лістинг 2: Hexdump двійкового файлу
$ hd / usr / bin / touch | голова -500000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ЕЛЬФ… |
00000010 02 00 3e 00 01 00 00 00 e3 25 40 00 00 00 00 00 |…>…% @… |
00000020 40 00 00 00 00 00 00 00 28 e4 00 00 00 00 00 00 | @… (… |
00000030 00 00 00 00 40 00 38 00 09 00 40 00 1b 00 1a 00 | [захищено електронною поштою] @… |
00000040 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 00 | [захищено електронною поштою] |
Debian GNU / Linux пропонує команду readelf, яка надається в пакеті GNU 'binutils'. У супроводі перемикача -h (коротка версія “-file-header”) він чудово відображає заголовок файлу ELF. Лістинг 3 ілюструє це для команди touch.
.Лістинг 3: Відображення заголовка файлу ELF
$ readelf -h / usr / bin / touchЗаголовок ELF:
Магія: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Клас: ELF64
Дані: доповнення 2, мало ендіан
Версія: 1 (поточна)
OS / ABI: UNIX - система V
Версія ABI: 0
Тип: EXEC (виконуваний файл)
Машина: Advanced Micro Devices X86-64
Версія: 0x1
Адреса точки входу: 0x4025e3
Початок заголовків програми: 64 (байт у файл)
Початок заголовків розділів: 58408 (байти у файл)
Прапори: 0x0
Розмір цього заголовка: 64 (байти)
Розмір заголовків програми: 56 (байт)
Кількість заголовків програми: 9
Розмір заголовків розділів: 64 (байти)
Кількість заголовків розділів: 27
Індекс таблиці рядків заголовка розділу: 26
Заголовок програми
Заголовок програми показує сегменти, що використовуються під час виконання, і повідомляє системі, як створити образ процесу. Заголовок з Лістингу 2 показує, що файл ELF складається з 9 заголовків програми, розмір яких становить 56 байт кожен, а перший заголовок починається з байта 64.
Знову ж таки, команда readelf допомагає витягти інформацію з файлу ELF. Перемикач -l (скорочення від -program-headers або -segments) показує більше деталей, як показано в лістингу 4.
.Лістинг 4: Відображення інформації про заголовки програм
$ readelf -l / usr / bin / touchТип файлу Elf - EXEC (виконуваний файл)
Точка входу 0x4025e3
Є 9 заголовків програм, починаючи зі зсуву 64
Заголовки програми:
Введіть Offset VirtAddr PhysAddr
FileSiz MemSiz Прапори Вирівняти
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Запит інтерпретатора програми: / lib64 / ld-linux-x86-64.так.2]
ЗАВАНТАЖИТИ 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000d494 0x000000000000d494 R E 200000
ЗАВАНТАЖИТИ 0x000000000000de10 0x000000000060de10 0x000000000060de10
0x0000000000000524 0x0000000000000748 RW 200000
ДИНАМІЧНА 0x000000000000de28 0x000000000060de28 0x000000000060de28
0x00000000000001d0 0x00000000000001d0 RW 8
ПРИМІТКА 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
Розділ для відображення сегментів:
Розділити сегменти…
00
01 .інтерп
02 .інтерп .Примітка.ABI-мітка .Примітка.гну.build-id .гну.хеш .динсим .динстр .гну.версія .гну.version_r .віднос.дин .віднос.plt .у цьому .plt .текст .фіні .родата .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .динамічний .здобули .здобули.plt .даних .bss
04 .динамічний
05 .Примітка.ABI-мітка .Примітка.гну.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .динамічний .здобули
Заголовок розділу
Третя частина структури ELF - це заголовок розділу. Він призначений для переліку окремих розділів двійкового файлу. Перемикач -S (скорочення від -section-headers або -sections) перелічує різні заголовки. Що стосується команди дотику, тут є 27 заголовків розділів, і в лістингу 5 показані лише перші чотири з них, а також останній. Кожен рядок охоплює розмір розділу, тип розділу, а також його адресу та зміщення пам’яті.
.Лістинг 5: Деталі розділу, розкриті readelf
$ readelf -S / usr / bin / touchЄ 27 заголовків розділів, починаючи зі зміщення 0xe428:
Заголовки розділів:
[Nr] Ім'я Тип Адреса Зсув
Розмір EntSize Прапори Посилання Інформація Вирівняти
[0] НУЛЬ 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[2] .Примітка.ABI-тег ПРИМІТКА 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[3] .Примітка.гну.build-i ПРИМІТКА 0000000000400274 00000274
..
..
[26] .shstrtab STRTAB 0000000000000000 0000e334
00000000000000ef 0000000000000000 0 0 1
Ключ до прапорів:
W (запис), A (виділення), X (виконання), M (злиття), S (рядки), l (великий)
I (інформація), L (порядок посилань), G (група), T (TLS), E (виключає), x (невідомо)
O (потрібна додаткова обробка ОС) o (конкретно для ОС), p (для процесора)
Інструменти для аналізу файлу ELF
Як ви могли зауважити з наведених вище прикладів, GNU / Linux розроблений рядом корисних інструментів, які допоможуть вам проаналізувати файл ELF. Перший кандидат, на якого ми подивимось, - це файлова утиліта.
файл відображає основну інформацію про файли ELF, включаючи архітектуру набору команд, для якої призначений код у переміщуваному, виконуваному або спільному об'єктному файлі. У списку 6 він повідомляє, що / bin / touch - це 64-розрядний виконуваний файл, що слідує за стандартною базою Linux (LSB), динамічно пов’язаний та створений для ядра GNU / Linux версії 2.6.32.
.Лістинг 6: Основна інформація за допомогою файлу
$ file / bin / touch/ bin / touch: виконуваний 64-розрядний LSB ELF, x86-64, версія 1 (SYSV), динамічно пов'язаний, інтерпретатор / lib64 / l,
для GNU / Linux 2.6.32, BuildID [sha1] = ec08d609e9e8e73d4be6134541a472ad0ea34502, позбавлений
$
Другий кандидат - readelf. Він відображає детальну інформацію про файл ELF. Список комутаторів порівняно довгий і охоплює всі аспекти формату ELF. Використання перемикача -n (скорочення від -notes) У лістингу 7 показані лише розділи приміток, які існують у стилі файлу - тег версії ABI та бітстринг ідентифікатора збірки.
.Лістинг 7: Відображення вибраних розділів файлу ELF
$ readelf -n / usr / bin / touchВідображення приміток, знайдених із зміщенням файлу 0x00000254 довжиною 0x00000020:
Розмір даних власника Опис
GNU 0x00000010 NT_GNU_ABI_TAG (тег версії ABI)
ОС: Linux, ABI: 2.6.32
Відображення приміток, знайдених із зміщенням файлу 0x00000274 довжиною 0x00000024:
Розмір даних власника Опис
GNU 0x00000014 NT_GNU_BUILD_ID (унікальний бітстринг ідентифікатора збірки)
Ідентифікатор збірки: ec08d609e9e8e73d4be6134541a472ad0ea34502
Зверніть увагу, що в програмах Solaris та FreeBSD утиліта elfdump [7] відповідає readelf. Станом на 2019 рік не було нового випуску чи оновлення з 2003 року.
Номер три - пакет із назвою elfutils [6], який є суто доступним для Linux. Він надає альтернативні інструменти GNU Binutils, а також дозволяє перевіряти файли ELF. Зверніть увагу, що всі назви службових програм, що містяться в пакеті, починаються з eu для "elf utils".
І останнє, але не менш важливе, ми згадаємо objdump. Цей інструмент схожий на readelf, але фокусується на об'єктних файлах. Він надає подібний спектр інформації про файли ELF та інші формати об'єктів.
.Лістинг 8: Інформація про файл, вилучена objdump
$ objdump -f / bin / touch/ bin / touch: формат файлу elf64-x86-64
архітектура: i386: x86-64, прапори 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
початкова адреса 0x00000000004025e3
$
Існує також програмний пакет під назвою "elfkickers" [9], який містить інструменти для зчитування вмісту файлу ELF, а також маніпулювання ним. На жаль, кількість випусків досить низька, і тому ми просто згадуємо про це, а не наводимо подальших прикладів.
Натомість, як розробник, ви можете поглянути на "pax-utils" [10,11]. Цей набір утиліт надає ряд інструментів, які допомагають перевірити файли ELF. Як приклад, dumpelf аналізує файл ELF і повертає файл заголовка C, що містить деталі - див. Малюнок 2.
Висновок
Завдяки поєднанню розумного дизайну та чудової документації формат ELF працює дуже добре, і все ще використовується після 20 років. Показані вище утиліти дають змогу переглянути уявлення про файл ELF і дозволити з’ясувати, що робить програма. Це перші кроки для аналізу програмного забезпечення - щасливий злом!
Посилання та посилання
- [1] Виконуваний та зв’язаний формат (ELF), Вікіпедія
- [2] Фуксія ОС
- [3] Порівняння форматів виконуваних файлів, Вікіпедія
- [4] Linux Foundation, посилання на технічні характеристики
- [5] Сіро Сантіллі: Підручник ELF Hello World
- [6] elfutils пакет Debian
- [7] elfdump
- [8] Майкл Болен: 101 файл ELF у Linux: Розуміння та аналіз
- [9] ельфіки
- [10] Загартовані / утиліти PaX
- [11] pax-utils, пакет Debian
Подяка
Письменник висловлює подяку Акселю Беккерту за підтримку щодо підготовки цієї статті.