Контакты

Для связи с нами можно использовать:
irc://irc.chatnet.ru:#gfs
icq://546460
email://cobalt[@]gfs-team.ru

Все материалы предоставлены только с ознакомительной целью
ГлавнаяСтатьиВирусологияELF. Структура файлов
© Burgeres 16.05.2008

От автора.

Данный материал открывает цикл статей, посвященный устройству elf формата файлов. Основная причина того, что он (материал) был написан – это некоторый дефицит русскоязычной литературы, касающейся освещаемого здесь вопроса. Безусловно, существуют отдельные заметки и переводы трудов наших заграничных коллег. Но в большинстве случаев elf-формат не является главным предметом разговора, а лишь рассматривается в контексте основной темы, поэтому сбор нужной информации занимает значительное время. Это увлекательное и весьма познавательное занятие, однако, бывают ситуации, когда нет времени перебирать десятки страниц и сотни ссылок в поисках необходимых данных. В такие моменты неплохо иметь под рукой что-то вроде справочника.

Этот документ был составлен именно с тем расчетом, чтобы в любое время можно было к нему вернуться и посмотреть нужное значение или определение. Поэтому не стоит ждать особых литературных изысков и отборных художественных приёмов. За основу была взята официальная документация по elf-формату. Ссылки на неё, а так же на другие источники будут опубликованы в конце.

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

Общее устройство.

Формат ELF был разработан и представлен Лабораторией Юникс (UNIX System Laboratories), как часть интерфейса ABI (Application Binary Interface - ABI). Затем он был выбран в качестве основного файлового формата для работы машин с 32-битным процессором. Elf достаточно быстро набрал популярность и в настоящее время успешно работает и на 64-битных процессорах. Он является основным файловым форматом во всех Unix и Unix-подобных операционных системах, при этом постепенно «захватывая» мобильные платформы.

Elf был призван упростить разработку, предоставляя программистам чёткую и понятную структуру файла и, соответственно, стандартизировать эту самую разработку. Наблюдая нынешнее положение дел, можно с уверенностью сказать, что все надежды, связанные с этим форматом, себя оправдали.

Общую структуру elf файла можно изобразить следующим образом:

Как видите, структуру файла можно и нужно рассматривать с двух сторон: со стороны компоновщика (линкера) и со стороны загрузчика. Любой файл состоит из:

• elf-заголовка (Elf header), в котором указаны общие характеристики файла: тип файла, его версия, адрес загрузки и прочие;
• таблицы программных заголовков (Program header table), которая служит для описания сегментов файла;
• таблицы заголовков секций (Section header table), которая, как несложно догадаться, характеризует секции файла.

Во всём этом взгляды загрузчика и компоновщика на структуру файла совпадают. Но отличия всё же есть. Дело в том, что загрузчик рассматривает оставшуюся часть файла, как набор сегментов, а компоновщик видит в нём непрерывную череду секций. То есть одинаковый двоичный код эти двое видят совершенно по-разному. При этом сегмент и секция вовсе не должны являться одинаковыми единицами - они не схожи ни по размеру (в отдельности), ни по общему количеству. Зачастую один сегмент включает в себя несколько секций.

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

Вот и получается, что эти таблицы присутствуют в файле “опционально” и зависят от его типа. То есть объектные файлы совершенно не нуждаются в таблице программных заголовков, а исполняемые легко обходятся без заголовков секций. Правда тут есть одно “Но”: файлы, не имеющие таблицы программных секций делают свою отладку весьма проблематичной – тот же GDB наотрез отказывается работать с такими. Поэтому можно сделать вывод о том, что присутствие “лишней” таблички будет совершенно нелишним, по крайней мере, на стадии отладки приложения.

Elf header.

Любой файл elf-формата начинается с elf заголовка (далее иногда именуемый файловым, что справедливо, ведь мы рассматриваем только один конкретный формат файлов).

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

Так, e_ident представляет собой массив, размер которого на данный момент составляет 16 байтов. Из этих 16 реально имеют смысл максимум 10. Вот о них-то мы и поговорим.

Первые четыре байта хоть и называются магическим числом, на деле представляют собой обыкновенную сигнатуру, которая в шестнадцатиричном виде выглядит следующим образом “7F 45 4C 46”, где каждый байт является кодом ASCII-символа. Первый – “¦”, второй – “E”, третий - ”L”, четвёртый - ”F”. Байт EI_CLASS определяет класс “машины”, для которой предназначен файл. Она может быть либо 32-х битной, либо 64-х битной.


------------------------------------------------------
ELFCLASSNONE – 0;
ELFCLASS32 – 1 – 32-х битная машина;
ELFCLASS64 – 2 – 64-х битная машина.
------------------------------------------------------

Шестой байт - EI_DATA - определяет порядок следования байтов в файле. Это может быть порядок Little-endian, когда байты располагаются от младшего к старшему, и Big-endian – от старшего к младшему.


------------------------------------------------------
ELFDATANONE - 0;
ELFDATA2LSB - 1 - байты следуют в порядке Little-endian;
ELFDATA2MSB - 2 - байты располагаются в порядке Big-endian. 
------------------------------------------------------

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

Как видно из рисунка, при порядке следования “от младшего к старшему”, младшие байты располагаются по меньшим адресам. При обратном порядке – самый младший байт, напротив, находится по большему адресу, а старшие байты располагаются по меньшим адресам.

Остаётся добавить, что процессоры Intel используют Little-endian, и если файл предназначен для работы на таком процессоре, значение байта EI_DATA должно быть равно единице.

EI_VERSION определяет номер версии заголовка. Вообще весьма странное значение, так как фактически оно не содержит никакого номера версии, а может равняться лишь 0 (EV_NONE) – некорректное значение, и 1 (EV_CURRENT) – корректное значение. Надо думать, что лучше, чтобы оно было корректным.

Байт EI_OSABI является седьмым по счёту байтом массива и определяет операционную систему и ABI, для которых предназначен файл. Собственно, что такое операционная система, думается, знает каждый, кто читает этот материал. А вот что такое ABI? ABI расшифровывается, как Application Binary Interface, а на русский это можно перевести примерно, как: «Прикладной двоичный интерфейс». Этот интерфейс являет собой некий стандарт. Так, к нему относятся: конвенция системных вызовов, т.е. то, как происходит передача параметров этим вызовам, а так же как осуществляются сами вызовы, какие вызовы вообще существуют и применяются в данной конкретной операционной системе и пр. У каждой операционной системы есть своё ABI. И в большинстве случаев ABI разных систем - различны. Это одна из причин, по которой невозможно написать на ассемблере программу, которая бы успешно функционировала и в Linux, и во FreeBSD. Более того, часто это невозможно даже для разных дистрибутивов Linux, в то время как в Си подобная переносимость и совместимость реализуется за счёт низкоуровневых библиотек и компилятора. Вообще, программирование на ассемблере, переносимость и прочие разные являются, опять же, отдельными темами, о которых мы обязательно поговорим, но не в этот раз.

Как вы наверняка уже догадались, существует действительно немало вариантов различных бинарных интерфейсов:


------------------------------------------------------
ELFOSABI_SYSV  - 0 – UNIX System V ABI;
ELFOSABI_HPUX – 1 – HP-UX operating system ABI;
ELFOSABI_NETBSD – 2 – Nx operating system ABI;
ELFOSABI_LINUX – 3 – 3GNU/Linux operating system ABI;
ELFOSABI_HURD – 4 – GNU/Hurd operating system ABI;
ELFOSABI_86OPEN – 5 – 86Open Common IA32 ABI;
ELFOSABI_SOLARIS – 6 – Solaris operating system ABI;
ELFOSABI_MONTEREY – 7 – Monterey project ABI;
ELFOSABI_IRIX – 8 – IRIX operating system ABI;
ELFOSABI_FREEBSD – 9 – Fx operating system ABI;
ELFOSABI_TRU64 – 10 – TRU64 UNIX operating system ABI;
ELFOSABI_MODESTO – 11 – Novell Modesto;
ELFOSABI_OPENBSD – 12 – OpenBSD;
ELFOSABI_OPENVMS – 13 – Open VMS;
ELFOSABI_NSK –  14 – HP Non-Stop Kernel;
ELFOSABI_ARM – 97 – ARM architecture ABI;
ELFOSABI_STANDALONE – 255 – Standalone (embedded) ABI.
------------------------------------------------------

Хочется добавить, что во многих структурах файла elf формата присутствуют такие поля, значение которых зависит от операционной системы, а соответственно и от данного байта. Поэтому правильность его значения трудно переоценить. Байт EI_ABIVERSION определяет версию ABI (да-да, у него ещё и версии имеются). Значение этого байта зависит от значения предыдущего и применяется для выявления несовместимых версий двоичного интерфейса.

На этом рабочие байты заканчиваются. EI_PAD является началом зарезервированных байтов. Эти байты равны нулю и игнорируются загрузчиком. В будущем многие из них обретут своё назначение, а пока они только прибавляют веса файлу. Байт EI_NIDENT завершает массив e_ident. Именно по нему мы можем узнать, что достигли конца массива.

Следующим полем структуры elf header является двухбайтовое значение e_type, которое располагается непосредственно за массивом и определяет тип файла. Это может быть исполняемый файл (аналог exe), разделяемая библиотека (аналог dll), объектный файл (relocatable file) или файл дампа памяти (core file). У каждого из них есть свои особенности. Не стоит забывать об этом и пренебрегать дополнительной информацией по конкретным типам файлов.


------------------------------------------------------
ET_NONE - 0;
ET_REL – 1 – объектный файл;
ET_EXEC – 2 – исполняемый файл; 
ET_DYN – 3 – разделяемая библиотека;
ET_CORE – 4 – файл дампа памяти;
ET_LOOSE – OXFE00 – зависит от операционной системы;
ET_HIOS – 0XFEFF – зависит от операционной системы;
ET_LOPROC – 0xFF00 – зависит от процессора;
ET_HIPROC – 0xFFFF – зависит от процессора.
------------------------------------------------------

Поле e_machine определяет архитектуру процессора, для которого предназначен файл. Все следующие значения, а соответственно и виды процессоров приведены, так сказать, для полноты повествования. Могу поспорить, что о многих из них вы никогда не слышали и не услышите. Но наука требует жертв, точности и жертв ради точности. Спешите видеть:


------------------------------------------------------
EM_NONE – 0; 
EM_M32 – 1 - AT&T WE 32100; 
EM_SPARC – 2 – SPARC; 
EM_386 – 3 – Intel 80386; 
EM_68K – 4 – Motorola 68000; 
EM_88K – 5 – Motorola 88000; 
EM_486 – 6 – Intel i486;
EM_860 – 7 - Intel 80860; 
EM_MIPS – 8 - MIPS I Architecture; 
EM_S370 – 9 - IBM System/370 Processor; 
EM_MIPS_RS3_LE - 10 - MIPS RS3000 Little-endian;
EM_PARISC – 15 – Hewlett-Packard PA-RISC; 
EM_VPP500 – 17 – Fujitsu VPP500; 
EM_SPARC32PLUS – 18 – SPARC v8plus;
EM_960 – 19 – Intel 80960; 
EM_PPC – 20 – PowerPC 32-bit;
EM_PPC64 – 21 – PowerPC 64-bit;
EM_S390 – 22 – IBM System / 390;
EM_V800 – 36 – NEC V800; 
EM_FR20 – 37 – Fujitsu FR20; 
EM_RH32 – 38 – TRW RH-32; 
EM_RCE – 39 – Motorola RCE; 
EM_ARM – 40 – Advanced RISC Machines ARM; 
EM_ALPHA_STD – 41 – Digital Alpha (standard value);
EM_SH – 42 – Hitachi SH; 
EM_SPARCV9 – 43 – SPARC Version 9; 
EM_TRICORE – 44 – Siemens Tricore embedded processor; 
EM_ARC – 45 – Argonaut RISC Core, Argonaut Technologies Inc; 
EM_H8_300 – 46 – Hitachi H8/300; 
EM_H8_300H – 47 – Hitachi H8/300H; 
EM_H8S – 48 – Hitachi H8S; 
EM_H8_500 – 49 – Hitachi H8/500; 
EM_IA_64 – 50 – Intel IA-64 Processor;
EM_MIPS_X – 51 – Stanford MIPS-X; 
EM_COLDFIRE – 52 – Motorola ColdFire; 
EM_68HC12 – 53 – Motorola M68HC12; 
EM_MMA – 54 – Fujitsu MMA; 
EM_PCP – 55 – Siemens PCP; 
EM_NCPU – 56 – Sony nCPU embedded RISC processor; 
EM_NDR1 – 57 – Denso NDR1 microprocessor;
EM_STARCORE – 58 – Motorola Star*Core processor; 
EM_ME16 – 59 – Toyota ME16 processor; 
EM_ST100 – 60 – STMicroelectronics ST100 processor; 
EM_TINYJ – 61 – Advanced Logic Corp. TinyJ processor;
EM_X86_64 – 62 – Advanced Micro Devices x86 – 64.
------------------------------------------------------

Наверняка вы заметили, что некоторые числовые значения в этом списке пропущены. Эти значения являются резервом на будущее, ведь прогресс и производство не стоят на месте. И если вы не слышали о появление какого-то нового процессора – это не значит, что его нет в природе. Этот список, думаю, стал лучшим тому подтверждением. В поле e_entry содержится точка входа в программу, т.е. адрес, с которого начинается выполнение кода программы. Обычно это значение равно 0x8048000 – для исполняемых файлов или 0x40000000 – для разделяемых библиотек.

Итак, осталось рассмотреть ещё 9 полей. Все они, за исключением e_flags (поле должно содержать флаги процессора, но для процессоров фирмы Intel они не определены и игнорируются) так или иначе, характеризуют таблицу программных заголовков и заголовков секций.

Поля e_phoff и e_shoff содержат смещение таблиц относительно начала файла (ph – program header, sh – section header), e_phentsize и e_shentsize – размеры одной записи, а e_phnum и e_shnum – количество этих самых записей в соответствующих таблицах. E_shstrndx указывает расположение заголовка секции .shstrtab в таблице заголовков секций.

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

Т.е:
size of Program header table = e_phentsize x e_phnum;
size of Section header table = e_shentsize x e_shnum.

В случае отсутствия одной из таблиц все соответствующие ей значения будут равны 0. На этом мы завершаем своё первое знакомство с elf-форматом, но не прощаемся. В следующий раз мы поговорим о таблице программных заголовков, рассмотрим основные типы сегментов и их назначение.

Just 4 Fun!




© Burgeres 16.05.2008

e-Commerce Partners Network
=)ping? написал:

EM_ME16 – 59 – Toyota ME16 processor;
хек

zukalo написал:

Боян, я в гугле находил доки по структуре ELF как на русском, так и на англ (Например от TIS, на опеннете вроде тоже было). К тому же это не статья , а скорее заметка. Но всё равно автору респект за то, что разобрался.

Burgeres написал:

2 zukalo
Ну что касается английского: тут и искать-то нечего. Есть ман-страницы, куча работ зарубежных авторов (на одном только wasm'e их порядка пяти), есть официальная спецификация, в конце-то концов. А вот с русскими доками... С ними вам, видимо, повезло гораздо больше, чем мне. Из тех, что встречались запомнились две работы - это "Формат исполняемых файлов Lunix ELF" авторства Александра Фисуна и "Трассировка процессов с помощью Ptrace" Sandeep S в переводе Андрея Киселева. Но в первой некоторые моменты опущены, во второй формат файлов не является основной темой и приведён скорее для "первичного ознакомления" и лучшего понимания основной темы. Может "не там" смотрел.
В общем я был бы не прочь ознакомиться ещё с чем-то. Поэтому, если у вас сохранились те самые русскоязычные доки или ссылки на них - свяжитесь со мной по почте: Burgeres[собака]mail.ru.

cyber написал:

В Юникс компиляция ассемблера : cc file.s -o progname.

Ли написал:

а где же продолжение?

Ник:

Текст:
P Br B I Qute



Код: обновить
Последние комментарии
24.06.2017 21:15:55 DeweyAloma написал:
If you have a desire to learn how to earn from...
Ручная распаковка NeoLite 2.0
24.06.2017 14:43:35 Issacpam написал:
Ckaйп evg7773 Ламинин +38097-613-1437 Laminine LPGN в Хуст Закарпатье 28 usd...
Взлом домофонов
21.06.2017 04:39:52 PRO написал:
Взлом и подбор паролей на почтовых сервисах и социальных сетях. Работаем со всеми...
Взлом E-mail (email, почты, мыла)
Реклама

Тут должна была быть ваша реклама, но мы потеряли глиняную табличку с ее текстом. SapeId: 665044

Rambler's Top100