Перейти к содержимому

Elf что это

  • автор:

Перевод «elf» на русский

You should command the elf troops and make sure that the tree remains forever green after these battles.

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

Instead, an elf meditates in deep trance for 4 hours a day.
Вместо этого эльф медитирует в глубоком трансе в течение 4 часов в сутки.
Offer the elf a baby in exchange for one wish.
Предложить эльфу младенца в обмен на одно желание.

Each character — dwarf, elf, fighter and cleric — has his own abilities, from powerful magic to healing powers.

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

Fun game where you have to prevent the escape elf with a pot full of gold coins.
Забавная игра, где вы должны предотвратить побег эльфа с горшком полным золотых монет.
And the school bell sounds like an elf hitting a tiny triangle.
А звонок такой, будто эльф в треугольник стучит.
I could take a pic of myself dressed up as an elf.
Я могу снять себя, одетой в эльфа.

Kurse possesses a number of superhuman attributes as a result of his natural dark elf physiology and mystical augmentation.

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

I think I saw an elf in the forest.
Думаю, я видел в лесу эльфа.
I thought you’d be dressed as an elf, too.
Я думала, ты тоже оденешься эльфом.
The elf told me I’d be the world champion.
Эльф сказал мне, что я стану мировым чемпионом.
If it was green instead, you’d be a nice elf.
Будь он зеленым, ты бы был отличным эльфом.
If I could just get my little elf to sit down.
Хорошо бы, чтобы мой маленький эльф присел.
A hundred years is a mere blink in a life of an elf.
Сто лет — всего лишь миг в жизни эльфа.
And remember, even the smallest envelope is heavy for an elf.
И помни, даже самый маленький конверт тяжеловат для эльфа.
Because I’m the head elf. I don’t give bad news.
Потому, что я главный эльф, я не приношу плохих новостей.
Возможно неприемлемое содержание

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

Зарегистрируйтесь, чтобы увидеть больше примеров. Это просто и бесплатно
Ничего не найдено для этого значения.
Предложить пример
Больше примеров Предложить пример

Новое: Reverso для Windows

Переводите текст из любого приложения одним щелчком мыши .

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

Результатов: 1989 . Точных совпадений: 1989 . Затраченное время: 56 мс

Помогаем миллионам людей и компаний общаться более эффективно на всех языках.

Elf что это

В заголовочном файле определён формат ELF для исполняемых двоичных файлов. К таким файлам относятся обычные исполняемые файлы, перемещаемые объектные файлы, core-файлы и общие объекты. Исполняемый файл в формате ELF состоит из заголовка ELF, таблицы заголовков программы или таблицы заголовков разделов (или обеих таблиц). Заголовок ELF всегда расположен в начале файла. Расположение таблицы заголовков программы и таблицы заголовков разделов задаётся в заголовке ELF. В этих двух таблицах описывается всё остальное содержимое файла. Данный заголовочный файл описывает вышеупомянутые заголовки в виде структур C, а также включает описание структур динамических разделов, разделов перемещений и таблиц символов. Для каждой N-битной архитектуры используется соответствующий тип (N=32,64; ElfN может быть Elf32 или Elf64; uintN_t может быть uint32_t или uint64_t):

ElfN_Addr Беззнаковый адрес программы, uintN_t ElfN_Off Беззнаковое смещение в файле, uintN_t ElfN_Section Беззнаковый индекс раздела, uint16_t ElfN_Versym Беззнаковые данные о версии символа, uint16_t Elf_Byte unsigned char ElfN_Half uint16_t ElfN_Sword int32_t ElfN_Word uint32_t ElfN_Sxword int64_t ElfN_Xword uint64_t

(Замечание: В *BSD используется немного другая терминология. Так, Elf64_Half — удвоенный Elf32_Half, а Elf64Quarter — uint16_t. Чтобы не путаться, далее эти типы заменены на их явные типы.) Все структуры данных этого формата файлов следуют «естественному» размеру и принципам выравнивания соответствующего класса. Если требуется, структуры данных содержат явно указанные заполнители (padding) для выравнивания по 4-м байтам для 4-байтовых объектов, для доведения размера структур до кратного 4-м и т. д. Заголовок ELF описывается типом Elf32_Ehdr или Elf64_Ehdr:

#define EI_NIDENT 16 typedef struct < unsigned char e_ident[EI_NIDENT]; uint16_t e_type; uint16_t e_machine; uint32_t e_version; ElfN_Addr e_entry; ElfN_Off e_phoff; ElfN_Off e_shoff; uint32_t e_flags; uint16_t e_ehsize; uint16_t e_phentsize; uint16_t e_phnum; uint16_t e_shentsize; uint16_t e_shnum; uint16_t e_shstrndx; >ElfN_Ehdr;

Значения полей: e_ident Массив байт, описывающий как воспринимать файл, не зависит от типа процессора или остального содержимого файла. Всё в массиве описывается макросами, начинающимися с префикса EI_, которые могут иметь значения, начинающиеся с префикса ELF. Определены следующие макросы: EI_MAG0 Первый байт отличительного (magic) числа. Должен быть заполнен ELFMAG0. (0: 0x7f) EI_MAG1 Второй байт отличительного числа. Должен быть заполнен ELFMAG1. (1: ‘E’) EI_MAG2 Третий байт отличительного числа. Должен быть заполнен ELFMAG2. (2: ‘L’) EI_MAG3 Четвёртый байт отличительного числа. Должен быть заполнен ELFMAG3. (3: ‘F’) EI_CLASS В пятом байте задаётся архитектура двоичного файла: ELFCLASSNONE Неправильный класс. ELFCLASS32 32-битная архитектура. Поддерживаются машины с файлами и виртуальным адресным пространством до 4 гигабайт. ELFCLASS64 64-битная архитектура. EI_DATA В шестом байте задаётся порядок кодирования данных в файле, используемый в процессоре. В настоящее время поддерживаются: ELFDATANONE Неизвестный формат данных. ELFDATA2LSB Обратный порядок байт (little-endian) в дополнительном коде. ELFDATA2MSB Прямой порядок байт (big-endian) в дополнительном коде. EI_VERSION В седьмом байте указывается номер версии спецификации ELF: EV_NONE Неправильный номер версии. EV_CURRENT Текущая версия. EI_OSABI В восьмом байте указывается тип операционной системы и двоичного интерфейса приложений (ABI), для которой предназначен объект. Некоторые поля в других структурах ELF имеют флаги и значения, зависящие от платформы; интерпретация таких полей определяется значением данного байта. Пример: ELFOSABI_NONE Тоже что и ELFOSABI_SYSV. ELFOSABI_SYSV UNIX System V ABI. ELFOSABI_HPUX HP-UX ABI. ELFOSABI_NETBSD NetBSD ABI. ELFOSABI_LINUX Linux ABI. ELFOSABI_SOLARIS Solaris ABI. ELFOSABI_IRIX IRIX ABI. ELFOSABI_FREEBSD FreeBSD ABI. ELFOSABI_TRU64 TRU64 UNIX ABI. ELFOSABI_ARM ABI архитектуры ARM. ELFOSABI_STANDALONE Автономный (встраиваемый) ABI. EI_ABIVERSION В девятом байте указывается версия ABI, для которой предназначен объект. Это поле используется для разграничения несовместимых версий ABI. Интерпретация данного номера версии зависит от ABI, указанного в поле EI_OSABI. В приложениях, удовлетворяющих данной спецификации, используется значение 0. EI_PAD Начало заполнителя. Эти байты зарезервированы и устанавливаются в ноль. Программы, читающие заголовок, должны игнорировать их. Значение EI_PAD будет изменено, если понадобится задействовать неиспользуемые в данный момент байты. EI_NIDENT Размер массива e_ident. e_type В этом поле структуры содержится тип объектного файла: ET_NONE Неизвестный тип. ET_REL Перемещаемый файл. ET_EXEC Исполняемый файл. ET_DYN Динамический объект. ET_CORE Файл типа core. e_machine В этом поле содержится значение требуемой для файла архитектуры. Пример: EM_NONE Неизвестная машинная архитектура. EM_M32 AT&T WE 32100. EM_SPARC Sun Microsystems SPARC. EM_386 Intel 80386. EM_68K Motorola 68000. EM_88K Motorola 88000. EM_860 Intel 80860. EM_MIPS MIPS RS3000 (только с прямым порядком байт). EM_PARISC HP/PA. EM_SPARC32PLUS SPARC с расширенным набором инструкций. EM_PPC PowerPC. EM_PPC64 PowerPC, 64-битная. EM_S390 IBM S/390. EM_ARM Advanced RISC Machines. EM_SH Renesas SuperH. EM_SPARCV9 SPARC v9, 64-битная. EM_IA_64 Intel Itanium. EM_X86_64 AMD x86-64. EM_VAX DEC Vax. e_version В этом поле содержится версия файла: EV_NONE Неправильный номер версии. EV_CURRENT Текущая версия. e_entry В этом поле содержится виртуальный адрес, по которому система должна передать управление для запуска процесса. Если в файле нет такой точки входа, то значение поля равно 0. e_phoff В этом поле содержится файловое смещение в байтах для таблицы заголовков программы. Если в файле нет таблицы заголовков программы, то значение поля равно 0. e_shoff В этом поле содержится файловое смещение в байтах для таблицы заголовков разделов. Если в файле нет таблицы заголовков разделов, то значение поля равно 0. e_flags В этом поле содержатся специфичные для процессора флаги, относящиеся к файлу. Имена флагов имеют вид: EF_машинный_флаг. В настоящее время нет ни одного предопределённого флага. e_ehsize В этом поле содержится размер заголовка ELF в байтах. e_phentsize В этом поле содержится размер в байтах одного элемента таблицы заголовков программы в файле; все элементы имеют одинаковый размер. e_phnum В этом поле содержится количество элементов в таблице заголовков программы. Таким образом, произведение e_phentsize и e_phnum даёт размер таблицы в байтах. Если в файле нет заголовков программы, то e_phnum содержит 0. Если количество элементов в таблице заголовков программы больше или равно PN_XNUM (0xffff), это поле содержит значение PN_XNUM (0xffff) и реальное количество элементов таблицы заголовков программы хранится в поле sh_info начального элемента таблицы заголовков разделов. Иначе поле sh_info начального элемента содержит ноль. PN_XNUM Имеет значение 0xffff, самое большое количество, которое может иметь e_phnum, показывает, где расположено реальное количество заголовков программы. e_shentsize В этом поле содержится размер в байтах одного элемента таблицы заголовков разделов; все элементы имеют одинаковый размер. e_shnum В этом поле содержится количество элементов в таблице заголовков разделов. Таким образом, произведение e_shentsize и e_shnum даёт размер таблицы разделов в байтах. Если в файле нет заголовков разделов, то e_shnum содержит 0. Если количество элементов в таблице заголовков разделов больше или равно SHN_LORESERVE (0xff00), то значение e_shnum равно и реальное количество элементов таблицы заголовков разделов хранится в поле sh_size начального элемента таблицы заголовков разделов. Иначе поле sh_size начального элемента таблицы заголовков разделов имеет значение ноль. e_shstrndx В этом поле содержится индекс элемента в таблице заголовков разделов, указывающий на строковую таблицу названий разделов. Если в файле нет строковой таблицы названий разделов, то это поле содержит значение SHN_UNDEF. Если индекс строки имён разделов в таблице разделов больше или равен SHN_LORESERVE (0xff00), то в этом поле содержится значение SHN_XINDEX (0xffff) и реальный индекс строки имён разделов в таблице разделов хранится в поле sh_link начального элемента таблицы заголовков разделов. Иначе поле sh_link начального элемента таблицы заголовков разделов имеет значение ноль. SHN_UNDEF Этим значением помечается неопределённая, отсутствующая, неприменимая или другая нецелесообразная ссылка на раздел. Например, символ, «определённый» в разделе с номером SHN_UNDEF, является неопределённым символом. SHN_LORESERVE Это значение задаёт нижнюю границу диапазона зарезервированных индексов. SHN_LOPROC Значения больше данного или равные SHN_HIPROC зарезервированы для процессорно-ориентированной семантики. SHN_HIPROC Значения меньше данного или равные SHN_LOPROC зарезервированы для процессорно-ориентированной семантики. SHN_ABS Это значение указывает на абсолютное значение соответствующей ссылки. Например, символы, определённые в разделе с номером SHN_ABS, имеют абсолютные значения и не подвержены перемещению. SHN_COMMON Символы, определённые в разделе этого типа, являются общими символами, такими как Fortran COMMON или нераспределённые внешние переменные C. SHN_HIRESERVE Это значение задаёт верхнюю границу диапазона зарезервированных индексов между SHN_LORESERVE и SHN_HIRESERVE включительно; для значений из диапазона нет ссылок в таблице заголовков разделов. То есть, таблица заголовков разделов не содержит элементы по зарезервированным индексам. Таблица заголовков программы исполняемого или совместно используемого объектного файла представляет собой массив структур, каждая из которых описывает сегмент или содержит другую информацию, необходимую системе для подготовки программы к выполнению. Сегмент объектного файла содержит один или более разделов. Заголовки программы нужны только для исполняемых и совместно используемых объектных файлов. Размер заголовков программы указывается в файле в заголовке ELF в полях e_phentsize и e_phnum. Заголовок программы ELF описывается типом Elf32_Phdr или Elf64_Phdr, в зависимости от архитектуры:

typedef struct < uint32_t p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; uint32_t p_filesz; uint32_t p_memsz; uint32_t p_flags; uint32_t p_align; >Elf32_Phdr;
typedef struct < uint32_t p_type; uint32_t p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; uint64_t p_filesz; uint64_t p_memsz; uint64_t p_align; >Elf64_Phdr;

Основным отличием между 32-битным и 64-битным программным заголовком в структуре является расположение поля p_flags. p_type Это поле структуры Phdr определяет, какой тип сегмента описывает этот элемент массива или как воспринимать информацию данного элемента массива. PT_NULL Элемент массива не используется и значения других полей не определены. Это позволяет иметь в заголовке программы игнорируемые элементы. PT_LOAD Элемент массива определяет загружаемый сегмент, описываемый p_filesz и p_memsz. Байты из файла проецируются в начало сегмента памяти. Если размер сегмента памяти p_memsz больше чем размер файла p_filesz, то определяются «дополнительные» байты, содержащие значение 0, и их располагают за инициализированной областью сегмента. Размер файла не может быть больше размера памяти. Элементы загружаемых сегментов в таблице заголовков программы располагаются в порядке возрастания, их сортируют по полю p_vaddr. PT_DYNAMIC Элемент массива указывает на данные с информацией по динамической компоновке. PT_INTERP Элемент массива указывает на данные о расположении и размере пути (завершается null) вызываемого интерпретатора. Этот тип сегмента имеет смысл только для исполняемых файлов (хотя может быть и в динамических объектных файлах). Однако, в файле он не может указываться более одного раза. Если он задан, то должен находиться перед всеми элементами загружаемых сегментов. PT_NOTE Элемент массива указывает на расположение и размер вспомогательной информации. PT_SHLIB Данный тип сегмента зарезервирован, но имеет неопределённую семантику. Программы, в которых есть элемент массива такого типа, не соответствуют ABI. PT_PHDR Элемент массива, если есть, указывает на расположение и размер самой таблицы заголовков программы, и в файле и в образе программы в памяти. Данный тип сегмента не может встречаться в файле более одного раза. Кроме того, он может задаваться только если таблица заголовков программы является частью образа программы в памяти. Если он задан, то должен находиться до элементов загружаемых сегментов. PT_LOPROC Значения, больше данного или равные PT_HIPROC, зарезервированы для процессорно-ориентированной семантики. PT_HIPROC Значения меньше данного или равные PT_LOPROC зарезервированы для процессорно-ориентированной семантики. PT_GNU_STACK Расширение GNU, используемое ядром Linux для управления состоянием стека через флаги, настраивается в поле p_flags. p_offset Это поле содержит смещение от начала файла, по которому располагается первый байт сегмента. p_vaddr Это поле содержит виртуальный адрес, по которому располагается первый байт сегмента в памяти. p_paddr В системах, для которых важна физическая адресация, это поле зарезервировано для физического адреса сегмента. В BSD это поле не используется и должно быть равно нулю. p_filesz В этом поле содержится число байт занимаемое сегментом в файле. Оно может быть равно нулю. p_memsz В этом поле содержится число байт занимаемое сегментом в памяти. Оно может быть равно нулю. p_flags В этом поле содержится битовая маска флагов соответствующего сегмента: PF_X Исполняемый сегмент. PF_W Сегмент доступен для записи. PF_R Сегмент доступен для чтения. Сегмента кода (text segment) обычно имеет флаги PF_X и PF_R. Сегмент данных обычно имеет флаги PF_X, PF_W и PF_R. p_align В этом поле содержится значение согласно которому сегменты выровнены в памяти и в файле. У загружаемых сегментов процесса значения p_vaddr и p_offset должны быть кратны размеру страницы. Величины ноль и один означают, что выравнивание не требуется. В противном случае значение p_align должно быть положительным числом кратным степени двойки, а значение p_vaddr должно быть равно p_offset и кратным p_align. По таблице заголовков разделов можно найти расположение всех разделов в файле. Она представляет собой массив структур Elf32_Shdr или Elf64_Shdr. На начало таблицы заголовков разделов в файле указывает поле e_shoff заголовка ELF (в байтах). В e_shnum содержится количество элементов таблицы заголовков разделов. В e_shentsize содержится размер каждого элемента в байтах. Индекс элемента в таблице заголовков разделов указывает в этот массив. Некоторые индексы элемента в таблице заголовков разделов зарезервированы: начальный элемент и индексы от SHN_LORESERVE и до SHN_HIRESERVE. Начальный элемент используется в расширениях ELF для e_phnum, e_shnum and e_strndx; в других случаях, каждое поле начального элемента равно нулю. В объектном файле нет разделов с этими специальными индексами: SHN_UNDEF Этим значением помечается неопределённая, отсутствующая, неприменимая, или другая нецелесообразная ссылка на раздел. SHN_LORESERVE Это значение задаёт нижнюю границу диапазона зарезервированных индексов. SHN_LOPROC Значения больше данного или равные SHN_HIPROC зарезервированы для процессорно-ориентированной семантики. SHN_HIPROC Значения меньше данного или равные SHN_LOPROC зарезервированы для процессорно-ориентированной семантики. SHN_ABS Это значение указывает на абсолютное значение соответствующей ссылки. Например, символы, определённые относительно раздела с номером SHN_ABS, имеют абсолютные значения и не подвержены перемещению. SHN_COMMON Символы, определённые относительно такого раздела, являются общими символами, такими как Fortran COMMON или нераспределённые внешние переменные C. SHN_HIRESERVE Этим значением определяется верхняя граница диапазона зарезервированных индексов. В системе зарезервированы индексы между SHN_LORESERVE и SHN_HIRESERVE включительно. В таблице заголовков разделов нет элементов с зарезервированными индексами. Заголовок раздела имеет следующую структуру:

typedef struct < uint32_t sh_name; uint32_t sh_type; uint32_t sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; uint32_t sh_size; uint32_t sh_link; uint32_t sh_info; uint32_t sh_addralign; uint32_t sh_entsize; >Elf32_Shdr;
typedef struct < uint32_t sh_name; uint32_t sh_type; uint64_t sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; uint64_t sh_size; uint32_t sh_link; uint32_t sh_info; uint64_t sh_addralign; uint64_t sh_entsize; >Elf64_Shdr;

Существенной разницы между 32-битными и 64-битными заголовками разделов нет. sh_name В этом поле указывается название раздела. Его значением является индекс в строковой таблице заголовков разделов, дающий расположение строки, заканчивающейся null. sh_type В этом поле содержится тип содержимого раздела, определящий смысл. SHT_NULL Этим значением помечают неактивные разделы в заголовке. У такого элемента нетпривязанного раздела. Значения других полей заголовка раздела не определены. SHT_PROGBITS Этот раздел содержит информацию, задаваемую программой; её формат и смысл полностью определяется программой. SHT_SYMTAB В этом разделе содержится таблица символов. Обычно, раздел SHT_SYMTAB предоставляет символы для редактирования связей, хотя также может использоваться при динамической компоновке. Являясь полной таблицей символов может содержать символы, не требуемые для динамической компоновки. Объектный файл также может содержать раздел SHT_DYNSYM. SHT_STRTAB В этом разделе содержится таблица строк. В объектном файле может быть несколько разделов с таблицами строк. SHT_RELA В этом разделе содержатся элементы перемещения с явными добавками, такими как тип Elf32_Rela для 32-битного класса объектных файлов. Объектный файл может иметь несколько разделов перемещений. SHT_HASH В этом разделе содержится хэш-таблица символов. Объект, участвующий в динамической компоновке, должен иметь хэш-таблицу символов. В объектном файле должна быть только одна хэш-таблица. SHT_DYNAMIC В этом разделе содержится информация по динамической компоновке. В объектном файле должен быть только один динамический раздел. SHT_NOTE В этом разделе содержится информация, которая позволяет как-то описать данный файл. SHT_NOBITS Разделы этого типа не занимают пространства в файле, но слегка напоминают SHT_PROGBITS. Несмотря на то, что байт в нём нет, поле sh_offset содержит умозрительное файловое смещение. SHT_REL В этом разделе содержатся элементы перемещения без явных добавок, таких как тип Elf32_Rela для 32-битного класса объектных файлов. Объектный файл может иметь несколько разделов перемещений. SHT_SHLIB Данный тип сегмента зарезервирован, но имеет неопределённую семантику. SHT_DYNSYM В этом разделе содержится минимальный набор символов для динамической компоновки. В объектном файле также может быть раздел SHT_SYMTAB. SHT_LOPROC Все значения, начиная с этого и больше, по SHT_HIPROC включительно, зарезервированы для процессорно-ориентированной семантики. SHT_HIPROC Все значения, начиная с этого и меньше, по SHT_LOPROC включительно, зарезервированы для процессорно-ориентированной семантики. SHT_LOUSER Это значение указывает на нижнюю границу диапазона индексов, зарезервированного для пользовательских программ. SHT_HIUSER Это значение указывает на нижнюю границу диапазона индексов, зарезервированного для пользовательских программ. Разделы с типами, имеющими значение между SHT_LOUSER и SHT_HIUSER, могут использоваться приложениями не конфликтуя с имеющимися или будущими типами разделов, определяемых системой. sh_flags В этом поле указываются различные атрибуты раздела, задаваемые в виде однобитных флагов. Если бит флага установлен в sh_flags, то атрибут «активен» для раздела. Иначе атрибут «выключен» или не применяется. Не указанные атрибуты сбрасываются в ноль. SHF_WRITE В разделе содержатся данные, к которым при работе процесса нужен доступ на запись. SHF_ALLOC Этот раздел занимает память при работе процесса. Некоторые управляющие разделы не располагаются в образе памяти объектного файла. Этот атрибут выключен у таких разделов. SHF_EXECINSTR Этот раздел содержит исполняемые машинные инструкции. SHF_MASKPROC Все биты этой маски зарезервированы для процессорно-ориентированной семантики. sh_addr Если этот раздел появляется в образе памяти процесса, то это поле содержит адрес, по которому должен располагаться первый байт раздела. Иначе поле содержит ноль. sh_offset В этом поле содержится смещение в байтах от начала файла до первого байта раздела. Раздел типа SHT_NOBITS не занимает места в файле и его поле sh_offset содержит умозрительное размещение в файле. sh_size В этом поле содержится размер раздела в байтах. За исключением раздела с типом SHT_NOBITS, все разделы занимают sh_size байт в файле. Раздел с типом SHT_NOBITS может иметь ненулевой размер, но места в файле не занимает. sh_link В этом поле содержится ссылка-индекс в таблицу заголовков раздела, а интерпретация зависит от типа раздела. sh_info В этом поле содержится дополнительная информация, чья интерпретация зависит от типа раздела. sh_addralign Некоторые разделы имеют ограничения по выравниванию адресов. Если раздел содержит двойное слово, то система должна произвести выравнивание по двойному слову всего раздела. То есть, значение sh_addr должно быть таким, чтобы при делении по модулю sh_addralign получался ноль. Разрешены только ноль и положительные степени двойки. Величины ноль или один означают, что раздел не имеет ограничений по выравниванию. sh_entsize В некоторых разделах содержатся таблицы с элементами одинакового размера, например, таблица символов. Для таких разделов в данном поле указывается размер в байтах каждого элемента. Если раздел содержит таблицу с элементами разного размера, то это поле равно нулю. Программа и управляющая информация содержится в различных разделах: .bss В этом разделе содержатся неинициализированные данные, которые вносятся в образ программы в памяти. По определению, в начале выполнения программы система инициализирует эти данные нулями. Этот раздел имеет тип SHT_NOBITS и атрибуты SHF_ALLOC и SHF_WRITE. .comment В этом разделе содержится управляющая информация о версии. Он имеет тип SHT_PROGBITS и не имеет атрибутов. .ctors В этом разделе содержатся инициализированные указатели функций-конструкторов C++. Он имеет тип SHT_PROGBITS и атрибуты SHF_ALLOC и SHF_WRITE. .data В этом разделе содержатся инициализированные данные, которые вносятся в образ программы в памяти. Он имеет тип SHT_PROGBITS и атрибуты SHF_ALLOC и SHF_WRITE. .data1 В этом разделе содержатся инициализированные данные, которые вносятся в образ программы в памяти. Он имеет тип SHT_PROGBITS и атрибуты SHF_ALLOC и SHF_WRITE. .debug В этом разделе содержится информация для символьной отладки. Формат содержимого не определён. Этот раздел имеет тип SHT_PROGBITS и не имеет атрибутов. .dtors В этом разделе содержатся инициализированные указатели функций-деструкторов C++. Он имеет тип SHT_PROGBITS и атрибуты SHF_ALLOC и SHF_WRITE. .dynamic В этом разделе содержится информация о динамической компоновке. К атрибутам раздела будет добавлен бит SHF_ALLOC. В зависимости от процессора может быть установлен бит SHF_WRITE. Этот раздел имеет тип SHT_DYNAMIC. .dynstr В этом разделе содержатся строки, необходимые для динамической компоновки; чаще всего это строки, представляющие имена, связанные с элементами таблицы символов. Этот раздел имеет тип SHT_STRTAB и атрибут SHF_ALLOC. .dynsym В этом разделе содержится таблица символов для динамической компоновки. Этот раздел имеет тип SHT_DYNSYM и атрибут SHF_ALLOC. .fini В этом разделе содержатся исполняемые инструкции, которые вносятся в код завершения процесса. При нормальном завершении программы система передаёт выполнение коду из этого раздела. Этот раздел имеет тип SHT_PROGBITS и атрибуты SHF_ALLOC и SHF_EXECINSTR. .gnu.version В этом разделе содержится таблица версий символов, массив элементов ElfN_Half. Данный раздел имеет тип SHT_GNU_versym и атрибут SHF_ALLOC. .gnu.version_d В этом разделе содержатся определения версий символов, таблица структур ElfN_Verdef. Данный раздел имеет тип SHT_GNU_verdef и атрибут SHF_ALLOC. .gnu.version_r В этом разделе содержатся версии символов необходимых элементов, таблица структур ElfN_Verneed. Данный раздел имеет тип SHT_GNU_versym и атрибут SHF_ALLOC. .got В этом разделе содержится таблица глобальных перемещений. Он имеет тип SHT_PROGBITS. Набор используемых атрибутов зависит от процессора. .hash В этом разделе содержится хэш-таблица символов. Он имеет тип SHT_HASH и атрибут SHF_ALLOC. .init В этом разделе содержатся исполняемые инструкции, которые вносятся в код инициализации процесса. Когда программа запускается, система передаёт выполнение коду из этого раздела до вызова основной программы. Данный раздел имеет тип SHT_PROGBITS и атрибуты SHF_ALLOC и SHF_EXECINSTR. .interp В этом разделе содержится путь к интерпретатору программы. Если файл имеет загружаемый сегмент, который включает раздел, то в атрибуты раздела будет добавлен бит SHF_ALLOC. Иначе этот бит будет обнулён. Данный раздел имеет тип SHT_PROGBITS. .line В этом разделе содержатся информация о номерах строк для символьной отладки, которая описывает соответствие между исходным кодом программы и машинным кодом. Формат содержимого не определён. Данный раздел имеет тип SHT_PROGBITS и не имеет атрибутов. .note В этом разделе содержится информация в формате «Note Section». Данный раздел имеет тип SHT_NOTE. Типы атрибутов не используются. В «родных» исполняемых файлах OpenBSD обычно содержится раздел .note.openbsd.ident для их идентификации, что используется ядром для пропуска тестирования на необходимость эмуляции двоичных файлов ELF при загрузке файла. .note.GNU-stack Этот раздел используется в объектных файлах Linux для объявления атрибутов стека. Данный раздел имеет тип SHT_PROGBITS и единственный атрибут SHF_EXECINSTR. Он указывает компоновщику GNU на необходимость объектного файла иметь исполняемый стек. .plt В этом разделе содержится таблица компоновки процедур. Он имеет тип SHT_PROGBITS. Набор используемых атрибутов зависит от процессора. .relИМЯ В этом разделе содержится информация о перемещениях, описываемая далее. Если файл имеет загружаемый сегмент, включающий перемещение, то в атрибуты раздела добавится бит SHF_ALLOC. Иначе этот бит будет обнулён. По соглашению, «ИМЯ» указывает на раздел, к которому применяются перемещения. То есть раздел перемещений для .text обычно называется .rel.text. Данный раздел имеет тип SHT_REL. .relaNAME В этом разделе содержится информация о перемещениях, описываемая далее. Если файл имеет загружаемый сегмент, включающий перемещение, то в атрибуты раздела добавится бит SHF_ALLOC. Иначе этот бит будет обнулён. По соглашению, «ИМЯ» указывает на раздел, к которому применяются перемещения. То есть раздел перемещений для .text обычно называется .rela.text. Данный раздел имеет тип SHT_RELA. .rodata В этом разделе содержатся данные, доступные только для чтения, которые обычно вносятся в недоступный для записи сегмент образа процесса. Этот раздел имеет тип SHT_PROGBITS и атрибут SHF_ALLOC. .rodata1 В этом разделе содержатся данные, доступные только для чтения, которые обычно вносятся в недоступный для записи сегмент образа процесса. Этот раздел имеет тип SHT_PROGBITS и атрибут SHF_ALLOC. .shstrtab В этом разделе содержатся имена разделов. Он имеет тип SHT_STRTAB и не имеет атрибутов. .strtab В этом разделе содержатся строки, чаще всего представляющие имена, связанные с элементами таблицы символов. Если файл имеет загружаемый сегмент, который включает таблицу строк символов, то к разделу атрибутов будет добавлен бит SHF_ALLOC. Иначе этот бит будет обнулён. Данный раздел имеет тип SHT_STRTAB. .symtab В этом разделе содержится таблица символов. Если файл имеет загружаемый сегмент, который включает таблицу символов, то к разделу атрибутов будет добавлен бит SHF_ALLOC. Иначе этот бит будет обнулён. Данный раздел имеет тип SHT_SYMTAB. .text В этом разделе содержится «код (text)», то есть исполняемые инструкции программы. Он имеет тип SHT_PROGBITS и атрибуты SHF_ALLOC и SHF_EXECINSTR. В разделах с таблицами строк содержатся символьные последовательности, завершающиеся null, которые обычно называются строками. Объектный файл использует эти строки для имён символов и разделов. Он ссылается на строку посредством индекса в разделе таблицы строк. В первом байте с нулевым индексом задаётся байт null (‘\0’). Подобно этому, для обеспечения завершения null всех строк последний байт таблицы строк также содержит байт null. В таблице символов объектного файла содержится информация, необходимая для обнаружения и перемещения определённых в программе символов и ссылок. Индекс таблицы символов указывает на элемент из этого массива.

typedef struct < uint32_t st_name; Elf32_Addr st_value; uint32_t st_size; unsigned char st_info; unsigned char st_other; uint16_t st_shndx; >Elf32_Sym;
typedef struct < uint32_t st_name; unsigned char st_info; unsigned char st_other; uint16_t st_shndx; Elf64_Addr st_value; uint64_t st_size; >Elf64_Sym;

32-битная и 64-битная версии имеют одинаковые поля, различен только их порядок. st_name В этом поле содержится индекс на элемент в таблице строк символов объектного файла, которая содержит символьное представление имён символов. Если значение не равно нулю, то это индекс таблицы строк, по которому определяется имя символа. Иначе таблица символов не имеет имени. st_value В этом поле содержится значение соответствующего символа. st_size Со многими символами связываются определённые размеры. Это поле имеет значение ноль, если символ не имеет размера или его размер неизвестен. st_info В этом поле задаётся тип символа и атрибуты привязки: STT_NOTYPE Тип символа не определён. STT_OBJECT Символу соответствует объект данных. STT_FUNC Символу соответствует функция или другой исполняемый код. STT_SECTION Символу соответствует раздел. Элементы таблицы символов этого типа существуют, прежде всего, для перемещения и обычно имеют привязки STB_LOCAL. STT_FILE По соглашению, имя символа назначается согласно имени файла исходного кода для соответствующего объектного файла. Файловый символ имеет привязки STB_LOCAL, его индекс раздела SHN_ABS, и он предваряется другим символом STB_LOCAL файла, если он есть. STT_LOPROC Все значения, начиная с этого и больше, по STT_HIPROC включительно, зарезервированы для процессорно-ориентированной семантики. STT_HIPROC Все значения, начиная с этого и меньше, по STT_LOPROC включительно, зарезервированы для процессорно-ориентированной семантики. STB_LOCAL Локальные символы невидимы вне объектного файла, содержащего их определения. Локальные символы с теми же именами могут существовать в нескольких файлах не мешая друг другу. STB_GLOBAL Глобальные символы видимы во всех объектных файлах после объединения. Определение глобального символа в одном файле будет разрешать неопределённую ссылку в другом файле для того же символа. STB_WEAK Слабые символы (weak symbols) похожи на глобальные символы, но их определения имеют меньший приоритет. STB_LOPROC Все значения, начиная с этого и больше, по STB_HIPROC включительно, зарезервированы для процессорно-ориентированной семантики. STB_HIPROC Все значения, начиная с этого и меньше, по STB_LOPROC включительно, зарезервированы для процессорно-ориентированной семантики. Макросы для упаковки и распаковки полей привязки и типа: ELF32_ST_BIND(info) или ELF64_ST_BIND(info) извлекают привязку из значения st_info. ELF32_ST_TYPE(info) или ELF64_ST_TYPE(info)
извлекают тип из значения st_info. ELF32_ST_INFO(bind, type) или ELF64_ST_INFO(bind, type)
преобразуют привязку и тип в значение st_info. st_other Этим полем определяется видимость символа. STV_DEFAULT Правила видимости символов по умолчанию. STV_INTERNAL Скрытый класс, зависящий от процессора. STV_HIDDEN Символ недоступен в других модулях. STV_PROTECTED Невыгружаемый, не экспортируется. Эти макросы служат для извлечения типа видимости: ELF32_ST_VISIBILITY(other) или ELF64_ST_VISIBILITY(other) st_shndx Каждый элемент таблицы символов «определён» в отношении к некоторому разделу. Это поле содержит соответствующий индекс таблицы заголовков разделов. Перемещение — это процесс соединения символьных ссылок с символьными определениями. Перемещаемые файлы должны иметь информацию, которая описывает как нужно изменить их содержимое разделов, чтобы позволить исполняемым и динамическим объектным файлам содержать корректную информацию для образа процесса программы. Для этого существуют перемещения. Перемещаемые структуры, которым не нужна добавка:

typedef struct < Elf32_Addr r_offset; uint32_t r_info; >Elf32_Rel;
typedef struct < Elf64_Addr r_offset; uint64_t r_info; >Elf64_Rel;

Перемещаемые структуры, которым нужна добавка:

typedef struct < Elf32_Addr r_offset; uint32_t r_info; int32_t r_addend; >Elf32_Rela;
typedef struct < Elf64_Addr r_offset; uint64_t r_info; int64_t r_addend; >Elf64_Rela;

r_offset В этом поле задаётся расположение, по которому применяется действие по перемещению. Для файла, допускающего перемещения, значением является байтовое смещение от начала раздела до хранимого элемента, подвергаемого перемещению. Для исполняемого файла или динамического объекта значением является виртуальный адрес хранимого элемента, подвергаемого перемещению. r_info В этом поле указывается индекс таблицы символов с соблюдением того, что нужно выполнить перемещение и тип применяемого перемещения. Типы перемещений зависят от архитектуры процессора. Когда в коде есть ссылка на тип перемещения элемента перемещения или индекс таблицы символов, то имеется в виду результат применения ELF[32|64]_R_TYPE или ELF[32|64]_R_SYM, соответственно, к полю r_info. r_addend В этом поле указывается константа-добавка, используемая для вычисления значения, хранимого в поле перемещения. В разделе .dynamic содержится несколько структур, в которых содержится информация по динамической компоновке. Полем d_tag контролируется интерпретация d_un.

typedef struct < Elf32_Sword d_tag; union < Elf32_Word d_val; Elf32_Addr d_ptr; >d_un; > Elf32_Dyn; extern Elf32_Dyn _DYNAMIC[];
typedef struct < Elf64_Sxword d_tag; union < Elf64_Xword d_val; Elf64_Addr d_ptr; >d_un; > Elf64_Dyn; extern Elf64_Dyn _DYNAMIC[];

d_tag В этом поле могут содержаться следующие значения: DT_NULL Этим значением помечается конец динамического раздела DT_NEEDED Смещение в таблице строк на имя необходимой библиотеки DT_PLTRELSZ Размер в байтах перемещений PLT DT_PLTGOT Адрес PLT и/или GOT DT_HASH Адрес хэш-таблицы символов DT_STRTAB Адрес таблицы строк DT_SYMTAB Адрес таблицы символов DT_RELA Адрес таблицы перемещений Rela DT_RELASZ Размер в байтах таблицы Rela DT_RELAENT Размер в байтах элемента таблицы Rela DT_STRSZ Размер в байтах таблицы строк DT_SYMENT Размер в байтах элемента таблицы строк DT_INIT Адрес функции инициализации DT_FINI Адрес функции окончания DT_SONAME Смещение в таблице строк для имени динамического объекта DT_RPATH Смещение в таблице строк для пути поиска (устарело) DT_SYMBOLIC Уведомление для компоновщика, что нужно искать этот динамический объект до поиска символов в исполняемом файле DT_REL Адрес таблицы перемещений Rel DT_RELSZ Размер в байтах таблицы Rel DT_RELENT Размер в байтах элемента таблицы Rel DT_PLTREL Тип перемещения ссылок PLT (Rela или Rel) DT_DEBUG Не определено, используется для отладки DT_TEXTREL Отсутствие указывает, что перемещения не должны применяться к сегменту, недоступному на запись DT_JMPREL Адрес элементов перемещений исключительно для PLT DT_BIND_NOW Указать динамическому компоновщику, что нужно обработать все перемещения до передачи управления исполняемому файлу DT_RUNPATH Смещение в таблице строк для пути поиска библиотек DT_LOPROC Начало процессорно-ориентированной семантики DT_HIPROC Конец процессорно-ориентированной семантики d_val В этом поле указываются целые (integer) значения различного смысла. d_ptr В этом поле указываются программные виртуальные адреса. При интерпретации данных адресов, реальные адреса должны вычисляться на основе оригинального значения из файла и базового адреса памяти. Файлы не содержат перемещаемых элементов для местоположения этих адресов. _DYNAMIC Массив, содержащий все динамические структуры в разделе .dynamic. Автоматически заполняется компоновщиком.

ЗАМЕЧАНИЯ

Впервые ELF появился в System V. Формат ELF является утверждённым стандартом. Расширения для e_phnum, e_shnum и e_strndx соответствующих расширений Linux. Также они поддерживаются в Sun, BSD и AMD64; дополнительную информацию смотрите в разделе «СМОТРИТЕ ТАКЖЕ».

Введение в ELF-файлы в Linux: понимание и анализ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Структура

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

  1. заголовка ELF
  2. данных

заголовок 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.

$ hexdump -n 16 /bin/ps 0000000 457f 464c 0102 0001 0000 0000 0000 0000 0000010

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

Версия

Затем следует ещё одно магической значение «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 и все значения.

7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF. | 02 00 3e 00 01 00 00 00 a8 2b 40 00 00 00 00 00 |..>. +@. | 40 00 00 00 00 00 00 00 30 65 01 00 00 00 00 00 |@. 0e. | 00 00 00 00 40 00 38 00 09 00 40 00 1c 00 1b 00 |. @.8. @. |

(вывод 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 показывают детали устройства стека.

# scanelf -e /bin/ps TYPE STK/REL/PTL FILE ET_EXEC RW- R-- RW- /bin/ps # execstack -q /bin/ps - /bin/ps

Команды для просмотра программного заголовка:

  • 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.

.text

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

12 .text 0000a3e9 0000000000402120 0000000000402120 00002120 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE
.data

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

.rodata

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

.bss

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

[24] .data PROGBITS 00000000006172e0 000172e0 0000000000000100 0000000000000000 WA 0 0 8 [25] .bss NOBITS 00000000006173e0 000173e0 0000000000021110 0000000000000000 WA 0 0 32

Команды для просмотра секций и заголовков.

  • dumpelf
  • elfls -p /bin/ps
  • eu-readelf –section-headers /bin/ps
  • readelf -S /bin/ps
  • objdump -h /bin/ps
Группы секций

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

# readelf -g /bin/ps There are no section groups in this file.

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

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

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

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

$ file /bin/ps /bin/ps: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=2053194ca4ee8754c695f5a7a7cff2fb8fdd297e, stripped

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

$ ldd /bin/ps linux-vdso.so.1 => (0x00007ffe5ef0d000) libprocps.so.3 => /lib/x86_64-linux-gnu/libprocps.so.3 (0x00007f8959711000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f895934c000) /lib64/ld-linux-x86-64.so.2 (0x00007f8959935000)

Совет: Чтобы посмотреть дальнейшие зависимости, лучше использовать утилиту 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-бинарников.

scanelf -a /bin/ps TYPE PAX PERM ENDIAN STK/REL/PTL TEXTREL RPATH BIND FILE ET_EXEC PeMRxS 0755 LE RW- R-- RW- - - LAZY /bin/ps

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-файлов

Ес­ли в мире Windows исполня­емые фай­лы пред­став­лены в фор­мате Portable Executable (PE), то в Linux эта роль отве­дена фай­лам в фор­мате Executable and Linkable Format (ELF). Сегод­ня мы заг­лянем внутрь таких фай­лов, нем­ного поис­сле­дуем их струк­туру и узна­ем, как они устро­ены.

Сра­зу отме­чу, что в отли­чие от Windows в Linux от рас­ширения фай­ла не зависит прак­тичес­ки ничего (за сов­сем неболь­шим исклю­чени­ем). Тип и фор­мат фай­ла опре­деля­ется его внут­ренним содер­жимым и наличи­ем тех или иных атри­бутов, поэто­му фай­лы в фор­мате Executable and Linkable Format могут иметь любое рас­ширение.

Что нам понадобится

Во­обще, мож­но позави­довать людям, оби­тающим в мире Linux, — как пра­вило, в сис­теме «из короб­ки» идет боль­шое чис­ло ути­лит и прог­рамм, которые в Windows необ­ходимо где‑то искать и уста­нав­ливать допол­нитель­но, да еще и не всег­да бес­плат­но. В нашем слу­чае для ана­лиза ELF-фай­лов в Linux при­сутс­тву­ет впол­не сос­тоятель­ный арсе­нал встро­енных средств и ути­лит:

  • readelf — с помощью этой ути­литы мож­но прак­тичес­ки пол­ностью прос­матри­вать все пота­енные мес­та ELF-фай­лов в удо­бочи­таемом виде;
  • hexdump — прос­той прос­мот­рщик фай­лов в шес­тнад­цатерич­ном пред­став­лении (конеч­но, до hiew из мира Windows ему далеко, но, во‑пер­вых, он при­сутс­тву­ет в сис­теме по умол­чанию, а во‑вто­рых, дела­ет это совер­шенно бес­плат­но);
  • strings — с помощью этой извес­тной ути­литы мож­но уви­деть име­на всех импорти­руемых (или экспор­тиру­емых) фун­кций, а так­же биб­лиотек, из которых эти фун­кции импорти­рова­ны, наз­вания сек­ций и еще мно­го чего инте­рес­ного;
  • ldd — поз­воля­ет выводить име­на раз­деля­емых биб­лиотек, из которых импорти­руют­ся те или иные фун­кции, исполь­зуемые иссле­дуемой прог­раммой;
  • nm — может показы­вать таб­лицу имен из сос­тава отла­доч­ной информа­ции, которая добав­ляет­ся в ELF-фай­лы при их ком­пиляции (эта отла­доч­ная информа­ция с помощью коман­ды strip может быть уда­лена из фай­ла, и в этом слу­чае ути­лита nm ничем не поможет);
  • objdump — спо­соб­на вывес­ти информа­цию и содер­жимое всех эле­мен­тов иссле­дуемо­го фай­ла, в том чис­ле и в дизас­сем­бли­рован­ном виде.

Часть перечис­ленно­го (кро­ме hexdump и ldd) вхо­дит в сос­тав пакета GNU Binutils. Если это­го пакета в тво­ей сис­теме нет, его лег­ко уста­новить. К при­меру, в Ubuntu это выг­лядит сле­дующим обра­зом:

sudo apt install binutils

В прин­ципе, имея все перечис­ленное, мож­но уже прис­тупать к ана­лизу и иссле­дова­нию ELF-фай­лов без прив­лечения допол­нитель­ных средств. Для боль­шего удобс­тва и наг­ляднос­ти мож­но добавить к нашему инс­тру­мен­тарию извес­тный в кру­гах реверс‑инже­неров дизас­сем­блер IDA в вер­сии Freeware (этой вер­сии для наших целей будет более чем дос­таточ­но, хотя ник­то не зап­реща­ет вос­поль­зовать­ся вер­сиями Home или Pro, если есть воз­можность за них зап­латить).

Анализ заголовка ELF-файла в IDA Freeware

Так­же неп­лохо было бы исполь­зовать вмес­то hexdump что‑то поудоб­нее, нап­ример 010 Editor или wxHex Editor. Пер­вый hex-редак­тор — дос­той­ная аль­тер­натива Hiew для Linux (в том чис­ле и бла­года­ря воз­можнос­ти исполь­зовать в нем боль­шое количес­тво шаб­лонов для раз­личных типов фай­лов, сре­ди них и шаб­лон для пар­синга ELF-фай­лов). Одна­ко он небес­плат­ный (сто­имость лицен­зии начина­ется с 49,95 дол­лара, при этом есть 30-днев­ный три­аль­ный пери­од).

Анализ заголовка ELF-файла в 010 Editor

Го­воря о допол­нитель­ных инс­тру­мен­тах, которые облегча­ют ана­лиз ELF-фай­лов, нель­зя не упо­мянуть Python-пакет lief. Исполь­зуя этот пакет, мож­но писать Python-скрип­ты для ана­лиза и модифи­кации не толь­ко ELF-фай­лов, но и фай­лов PE и MachO. Ска­чать и уста­новить этот пакет получит­ся тра­дици­онным для Python-пакетов спо­собом:

pip install lief

Подопытные экземпляры

В Linux (да и во мно­гих дру­гих сов­ремен­ных UNIX-подоб­ных опе­раци­онных сис­темах) фор­мат ELF исполь­зует­ся в нес­коль­ких типах фай­лов.

  • Ис­полня­емый файл — содер­жит все необ­ходимое для соз­дания сис­темой обра­за про­цес­са и запус­ка это­го про­цес­са. В общем слу­чае это инс­трук­ции и дан­ные. Так­же в фай­ле может при­сутс­тво­вать опи­сание необ­ходимых раз­деля­емых объ­ектных фай­лов, а так­же сим­воль­ная и отла­доч­ная информа­ция. Исполня­емый файл может быть позици­онно зависи­мым (в этом слу­чае он гру­зит­ся всег­да по одно­му и тому же адре­су, для 32-раз­рядных прог­рамм обыч­но это 0x8048000 , для 64-раз­рядных — 0x400000 ) и позици­онно незави­симым исполня­емым фай­лом (PIE — Position Independent Execution или PIC — Position Independent Code). В этом слу­чае адрес заг­рузки фай­ла может менять­ся при каж­дой заг­рузке. При пос­тро­ении позици­онно незави­симо­го исполня­емо­го фай­ла исполь­зуют­ся такие же прин­ципы, как и при пос­тро­ении раз­деля­емых объ­ектных фай­лов.
  • Пе­реме­щаемый файл — содер­жит инс­трук­ции и дан­ные, при этом они могут быть ста­тичес­ки свя­заны с дру­гими объ­ектны­ми фай­лами, в резуль­тате чего получа­ется раз­деля­емый объ­ектный или исполня­емый файл. К это­му типу отно­сят­ся объ­ектные фай­лы ста­тичес­ких биб­лиотек (как пра­вило, для ста­тичес­ких биб­лиотек имя начина­ется с lib и при­меня­ется рас­ширение *. a ), одна­ко, как мы уже говори­ли, рас­ширение в Linux прак­тичес­ки ничего не опре­деля­ет. В слу­чае ста­тичес­ких биб­лиотек это прос­то дань тра­диции, а работос­пособ­ность биб­лиоте­ки будет обес­печена с любым име­нем и любым рас­ширени­ем.
  • Раз­деля­емый объ­ектный файл — содер­жит инс­трук­ции и дан­ные, может быть свя­зан с дру­гими переме­щаемы­ми фай­лами или раз­деля­емы­ми объ­ектны­ми фай­лами, в резуль­тате чего будет соз­дан новый объ­ектный файл. Такие фай­лы могут выпол­нять фун­кции раз­деля­емых биб­лиотек (по ана­логии с DLL-биб­лиоте­ками Windows). При этом в момент запус­ка прог­раммы на выпол­нение опе­раци­онная сис­тема динами­чес­ки свя­зыва­ет эту раз­деля­емую биб­лиоте­ку с исполня­емым фай­лом прог­раммы, и соз­дает­ся исполня­емый образ при­ложе­ния. Опять же тра­дици­онно раз­деля­емые биб­лиоте­ки име­ют рас­ширение *. so (от англий­ско­го Shared Object).
  • Файл дам­па памяти — файл, который содер­жит образ памяти того или ино­го про­цес­са на момент его завер­шения. В опре­делен­ных ситу­ациях ядро может соз­давать файл с обра­зом памяти ава­рий­но завер­шивше­гося про­цес­са. Этот файл так­же соз­дает­ся в фор­мате ELF, одна­ко мы о такого рода фай­лах говорить не будем, пос­коль­ку задача иссле­дова­ния дам­пов и содер­жимого памяти дос­таточ­но объ­емна и тре­бует отдель­ной статьи.

Для наших изыс­каний нам желатель­но иметь все воз­можные вари­анты исполня­емых фай­лов из перечис­ленных выше, чем мы сей­час и зай­мем­ся.

Делаем исполняемые файлы

Не будем выдумы­вать что‑то свер­хориги­наль­ное, а оста­новим­ся на клас­сичес­ком хел­ловор­лде на С:

int main ( int argc , char * argv [] ) < printf ( "Hello world" ) ;

Ком­пилиро­вать это дело мы будем с помощью GCC. Сов­ремен­ные вер­сии Linux, как пра­вило, 64-раз­рядные, и вхо­дящие в их сос­тав по умол­чанию средс­тва раз­работ­ки (в том чис­ле и ком­пилятор GCC) генери­руют 64-раз­рядные при­ложе­ния. Мы в сво­их иссле­дова­ниях не будем отдель­но вни­кать в 32-раз­рядные ELF-фай­лы (по боль­шому сче­ту отли­чий от 64-раз­рядных ELF-фай­лов в них не очень мно­го) и основные уси­лия сос­редото­чим имен­но на 64-раз­рядных вер­сиях прог­рамм. Если у тебя воз­никнет желание поэк­спе­римен­тировать с 32-раз­рядны­ми фай­лами, то при ком­пиляции в GCC нуж­но добавить опцию -m32 , при этом, воз­можно, пот­ребу­ется уста­новить биб­лиоте­ку gcc-multilib. Сде­лать это мож­но при­мер­но вот так:

sudo apt- get install gcc- multilib

Итак, назовем наш хел­ловорлд example. c (кста­ти, здесь как раз один из нем­ногих слу­чаев, ког­да в Linux рас­ширение име­ет зна­чение) и нач­нем с исполня­емо­го позици­онно зависи­мого кода:

gcc -no-pie example. c -o example_ no_ pie

Как ты уже догадал­ся, опция -no-pie как раз и говорит ком­пилято­ру соб­рать не позици­онно незави­симый код.

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

В целом мож­но выделить четыре эта­па работы GCC:

  • преп­роцес­сирова­ние;
  • тран­сля­ция в ассем­блер­ный код;
  • пре­обра­зова­ние ассем­блер­ного кода в объ­ектный;
  • ком­понов­ка объ­ектно­го кода.

Что­бы пос­мотреть на про­межу­точ­ный резуль­тат, к при­меру в виде ассем­блер­ного кода, исполь­зуй в GCC опцию -S :

gcc -S -masm = intel example. c

Об­рати вни­мание на два момен­та. Пер­вый — мы в дан­ном слу­чае не зада­ем имя выход­ного фай­ла с помощью опции -o (GCC сам опре­делит его из исходно­го, добавив рас­ширение *. s , что и озна­чает при­сутс­твие в фай­ле ассем­блер­ного кода). Вто­рой момент — опция -masm=intel , которая говорит о том, что ассем­блер­ный код в выход­ном фай­ле необ­ходимо генери­ровать с исполь­зовани­ем син­такси­са Intel (по умол­чанию будет син­таксис AT&T, мне же, как и, навер­ное, боль­шинс­тву, син­таксис Intel бли­же). Так­же в этом слу­чае опция -no-pie не име­ет смыс­ла, пос­коль­ку ассем­блер­ный код в любом слу­чае будет оди­нако­вый, а перено­симость обес­печива­ется на эта­пе получе­ния объ­ектно­го фай­ла и сбор­ки прог­раммы.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *