589 lines
25 KiB
Python
589 lines
25 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
===============================================================================
|
||
ELF Reader — аналог readelf для ELF64 (AMD64/x86-64)
|
||
===============================================================================
|
||
|
||
НАЗНАЧЕНИЕ
|
||
----------
|
||
Модуль читает и отображает заголовки исполняемых ELF-файлов в стиле утилиты
|
||
readelf из пакета GNU Binutils. Вывод на русском языке, формат максимально
|
||
приближен к оригинальному readelf -h -l -S.
|
||
|
||
Поддерживается только архитектура AMD64 (x86-64), little-endian, 64-битный
|
||
формат ELF.
|
||
|
||
ИСПОЛЬЗОВАНИЕ
|
||
-------------
|
||
python3 elfreader.py <путь_к_elf_файлу>
|
||
|
||
Пример:
|
||
python3 elfreader.py /bin/ls
|
||
python3 elfreader.py моя_программа.elf
|
||
|
||
ФУНКЦИОНАЛЬНОСТЬ
|
||
-----------------
|
||
Выводит три блока информации:
|
||
|
||
1. Заголовок ELF (readelf -h):
|
||
- Magic-число и класс (ELF64)
|
||
- Порядок байт (little-endian)
|
||
- Тип файла (EXEC, DYN, REL, CORE)
|
||
- Целевая машина (X86-64)
|
||
- Адрес точки входа
|
||
- Расположение и количество программных/секционных заголовков
|
||
- Индекс строковой таблицы секций
|
||
|
||
2. Заголовки разделов (readelf -S):
|
||
- Таблица всех секций с адресами, смещениями, размерами
|
||
- Флаги секций (W=запись, A=выделение памяти, X=исполнение, I=инфо)
|
||
- Поля ссылок, информации и выравнивания
|
||
- Легенда обозначений флагов
|
||
|
||
3. Заголовки программы/сегментов (readelf -l):
|
||
- Таблица сегментов с виртуальными/физическими адресами
|
||
- Файловый и памяти'ный размеры
|
||
- Флаги доступа (R=чтение, W=запись, E=исполнение)
|
||
- Соответствие сегментов секциям
|
||
|
||
ВЫВОДИМЫЕ ДАННЫЕ
|
||
-----------------
|
||
Все числовые значения выводятся в шестнадцатеричном виде (адреса, смещения,
|
||
размеры) или десятичном (счётчики, индексы). Флаги секций и сегментов
|
||
отображаются в сокращённой буквенной нотации.
|
||
|
||
ОГРАНИЧЕНИЯ
|
||
-----------
|
||
- Только ELF64 little-endian (архитектура x86-64)
|
||
- Не поддерживаются 32-битные ELF-файлы
|
||
- Не анализируются таблицы символов (.symtab, .dynsym)
|
||
- Не обрабатываются релокации (.rel, .rela)
|
||
- Не читаются отладочные секции (.debug_*)
|
||
- Не разбираются динамические секции (.dynamic)
|
||
- Не интерпретируются NOTE-секции
|
||
- Не выводится информация о версиях символов
|
||
- Не читается интерпретатор из секции .interp
|
||
|
||
ТРЕБОВАНИЯ
|
||
----------
|
||
- Python 3.6 или выше
|
||
- Только стандартная библиотека (struct, sys)
|
||
- Внешние зависимости отсутствуют
|
||
|
||
АРХИТЕКТУРА МОДУЛЯ
|
||
-------------------
|
||
Весь код организован в виде набора чистых функций без классов:
|
||
|
||
Чтение структур:
|
||
read_elf_header() - парсинг основного ELF-заголовка (64 байта)
|
||
read_section_headers() - парсинг заголовков секций (по 64 байта каждый)
|
||
read_program_headers() - парсинг программных заголовков (по 56 байт каждый)
|
||
get_section_name() - извлечение имени секции из строковой таблицы
|
||
|
||
Форматирование вывода:
|
||
format_elf_header() - форматирование ELF-заголовка в строку
|
||
format_sections() - форматирование таблицы секций
|
||
format_program_headers() - форматирование таблицы сегментов
|
||
format_segment_section_map() - форматирование связей сегменты->секции
|
||
|
||
Вспомогательные:
|
||
get_flags_description() - преобразование битовых флагов секции в строку
|
||
|
||
ФОРМАТ ELF64 (краткая справка)
|
||
-------------------------------
|
||
ELF-заголовок (Elf64_Ehdr): 64 байта
|
||
- e_ident[16]: magic (4 байта), класс (1), порядок байт (1), версия (1),
|
||
OS/ABI (1), ABI version (1), padding (7)
|
||
- e_type (2 байта): тип файла
|
||
- e_machine (2 байта): архитектура (0x3E = x86-64)
|
||
- e_version (4 байта): версия формата
|
||
- e_entry (8 байт): адрес точки входа
|
||
- e_phoff (8 байт): смещение таблицы программных заголовков
|
||
- e_shoff (8 байт): смещение таблицы заголовков секций
|
||
- e_flags (4 байта): флаги процессора
|
||
- e_ehsize (2 байта): размер этого заголовка
|
||
- e_phentsize (2 байта): размер одного программного заголовка
|
||
- e_phnum (2 байта): количество программных заголовков
|
||
- e_shentsize (2 байта): размер одного заголовка секции
|
||
- e_shnum (2 байта): количество заголовков секций
|
||
- e_shstrndx (2 байта): индекс секции со строковой таблицей имён секций
|
||
|
||
Заголовок секции (Elf64_Shdr): 64 байта
|
||
- sh_name (4 байта): смещение имени в .shstrtab
|
||
- sh_type (4 байта): тип секции
|
||
- sh_flags (8 байт): флаги (W=1, A=2, X=4, I=0x8)
|
||
- sh_addr (8 байт): виртуальный адрес в памяти
|
||
- sh_offset (8 байт): смещение данных в файле
|
||
- sh_size (8 байт): размер секции в байтах
|
||
- sh_link (4 байта): ссылка на другую секцию
|
||
- sh_info (4 байта): дополнительная информация
|
||
- sh_addralign (8 байт): выравнивание адреса
|
||
- sh_entsize (8 байт): размер элемента (для таблиц)
|
||
|
||
Программный заголовок (Elf64_Phdr): 56 байт
|
||
- p_type (4 байта): тип сегмента (LOAD, DYNAMIC, INTERP, ...)
|
||
- p_flags (4 байта): флаги доступа (R=4, W=2, E=1)
|
||
- p_offset (8 байт): смещение данных в файле
|
||
- p_vaddr (8 байт): виртуальный адрес загрузки
|
||
- p_paddr (8 байт): физический адрес
|
||
- p_filesz (8 байт): размер в файле
|
||
- p_memsz (8 байт): размер в памяти
|
||
- p_align (8 байт): выравнивание
|
||
|
||
ПРИМЕР ВЫВОДА
|
||
-------------
|
||
$ python3 elfreader.py программа.elf
|
||
|
||
Заголовок ELF:
|
||
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
|
||
Класс: ELF64
|
||
Данные: дополнение до 2, от младшего к старшему
|
||
Version: 1 (current)
|
||
OS/ABI: UNIX - System V
|
||
Версия ABI: 0
|
||
Тип: EXEC (Исполняемый файл)
|
||
Машина: Advanced Micro Devices X86-64
|
||
...
|
||
|
||
ИНТЕГРАЦИЯ
|
||
----------
|
||
Модуль можно использовать как самостоятельную утилиту:
|
||
|
||
python3 elfreader.py файл.elf
|
||
|
||
Или импортировать функции для программного анализа ELF-файлов:
|
||
|
||
from elfreader import read_elf_header, read_section_headers
|
||
|
||
with open('программа.elf', 'rb') as f:
|
||
hdr = read_elf_header(f)
|
||
print(f"Точка входа: 0x{hdr['e_entry']:x}")
|
||
|
||
СРАВНЕНИЕ С readelf
|
||
-------------------
|
||
Вывод elfreader.py максимально приближен к выводу readelf из GNU Binutils.
|
||
Основные отличия:
|
||
- Все заголовки на русском языке (Заголовок ELF, Заголовки разделов, ...)
|
||
- Незначительные различия в форматировании пробелов
|
||
- Отсутствует часть расширенной информации (символы, релокации, dynamic)
|
||
- Не отображается интерпретатор из секции .interp
|
||
|
||
Для полного анализа ELF-файлов рекомендуется использовать оригинальный
|
||
readelf или библиотеку pyelftools для программного доступа.
|
||
|
||
АВТОР И ЛИЦЕНЗИЯ
|
||
-----------------
|
||
Создано в образовательных целях для изучения формата ELF.
|
||
Свободное использование, модификация и распространение.
|
||
"""
|
||
|
||
import struct
|
||
import sys
|
||
|
||
# ---------- константы ELF64 ----------
|
||
ELFMAG = b'\x7fELF'
|
||
ELFCLASS64 = 2
|
||
ELFDATA2LSB = 1
|
||
EM_X86_64 = 0x3E
|
||
|
||
# Типы ELF (e_type)
|
||
ET_NONE = 0
|
||
ET_REL = 1
|
||
ET_EXEC = 2
|
||
ET_DYN = 3
|
||
ET_CORE = 4
|
||
|
||
ETYPE_NAMES = {
|
||
ET_NONE: 'NONE (Нет типа файла)',
|
||
ET_REL: 'REL (Перемещаемый файл)',
|
||
ET_EXEC: 'EXEC (Исполняемый файл)',
|
||
ET_DYN: 'DYN (Совместно используемый объектный файл)',
|
||
ET_CORE: 'CORE (Файл ядра)'
|
||
}
|
||
|
||
# Типы секций (sh_type)
|
||
SHT_NULL = 0
|
||
SHT_PROGBITS = 1
|
||
SHT_SYMTAB = 2
|
||
SHT_STRTAB = 3
|
||
SHT_RELA = 4
|
||
SHT_HASH = 5
|
||
SHT_DYNAMIC = 6
|
||
SHT_NOTE = 7
|
||
SHT_NOBITS = 8
|
||
SHT_REL = 9
|
||
SHT_SHLIB = 10
|
||
SHT_DYNSYM = 11
|
||
SHT_INIT_ARRAY = 14
|
||
SHT_FINI_ARRAY = 15
|
||
SHT_PREINIT_ARRAY = 16
|
||
SHT_GROUP = 17
|
||
SHT_SYMTAB_SHNDX = 18
|
||
SHT_NUM = 19
|
||
SHT_GNU_HASH = 0x6ffffff6
|
||
SHT_VERSYM = 0x6fffffff
|
||
SHT_VERNEED = 0x6ffffffe
|
||
SHT_VERDEF = 0x6ffffffd
|
||
|
||
SHT_NAMES = {
|
||
SHT_NULL: 'NULL',
|
||
SHT_PROGBITS: 'PROGBITS',
|
||
SHT_SYMTAB: 'SYMTAB',
|
||
SHT_STRTAB: 'STRTAB',
|
||
SHT_RELA: 'RELA',
|
||
SHT_HASH: 'HASH',
|
||
SHT_DYNAMIC: 'DYNAMIC',
|
||
SHT_NOTE: 'NOTE',
|
||
SHT_NOBITS: 'NOBITS',
|
||
SHT_REL: 'REL',
|
||
SHT_SHLIB: 'SHLIB',
|
||
SHT_DYNSYM: 'DYNSYM',
|
||
SHT_INIT_ARRAY: 'INIT_ARRAY',
|
||
SHT_FINI_ARRAY: 'FINI_ARRAY',
|
||
SHT_PREINIT_ARRAY: 'PREINIT_ARRAY',
|
||
SHT_GROUP: 'GROUP',
|
||
SHT_SYMTAB_SHNDX: 'SYMTAB_SHNDX',
|
||
SHT_NUM: 'NUM',
|
||
SHT_GNU_HASH: 'GNU_HASH',
|
||
SHT_VERSYM: 'VERSYM',
|
||
SHT_VERNEED: 'VERNEED',
|
||
SHT_VERDEF: 'VERDEF'
|
||
}
|
||
|
||
# Типы программных заголовков (p_type)
|
||
PT_NULL = 0
|
||
PT_LOAD = 1
|
||
PT_DYNAMIC = 2
|
||
PT_INTERP = 3
|
||
PT_NOTE = 4
|
||
PT_SHLIB = 5
|
||
PT_PHDR = 6
|
||
PT_TLS = 7
|
||
PT_GNU_EH_FRAME = 0x6474e550
|
||
PT_GNU_STACK = 0x6474e551
|
||
PT_GNU_RELRO = 0x6474e552
|
||
PT_GNU_PROPERTY = 0x6474e553
|
||
|
||
PT_NAMES = {
|
||
PT_NULL: 'NULL',
|
||
PT_LOAD: 'LOAD',
|
||
PT_DYNAMIC: 'DYNAMIC',
|
||
PT_INTERP: 'INTERP',
|
||
PT_NOTE: 'NOTE',
|
||
PT_SHLIB: 'SHLIB',
|
||
PT_PHDR: 'PHDR',
|
||
PT_TLS: 'TLS',
|
||
PT_GNU_EH_FRAME: 'GNU_EH_FRAME',
|
||
PT_GNU_STACK: 'GNU_STACK',
|
||
PT_GNU_RELRO: 'GNU_RELRO',
|
||
PT_GNU_PROPERTY: 'GNU_PROPERTY'
|
||
}
|
||
|
||
# OS/ABI идентификаторы
|
||
OSABI_NAMES = {
|
||
0: 'UNIX - System V',
|
||
1: 'HP-UX',
|
||
2: 'NetBSD',
|
||
3: 'GNU/Linux',
|
||
6: 'Solaris',
|
||
9: 'FreeBSD',
|
||
12: 'OpenBSD'
|
||
}
|
||
|
||
|
||
# ---------- функции чтения структур ----------
|
||
def read_elf_header(f):
|
||
"""Читает и возвращает словарь с полями ELF-заголовка (64-бит)."""
|
||
data = f.read(64)
|
||
if len(data) < 64:
|
||
raise ValueError("Слишком маленький файл для ELF64 заголовка")
|
||
|
||
fmt = '16s H H I Q Q Q I H H H H H H'
|
||
(e_ident, e_type, e_machine, e_version,
|
||
e_entry, e_phoff, e_shoff, e_flags,
|
||
e_ehsize, e_phentsize, e_phnum,
|
||
e_shentsize, e_shnum, e_shstrndx) = struct.unpack(fmt, data)
|
||
|
||
if e_ident[:4] != ELFMAG:
|
||
raise ValueError("Не ELF файл")
|
||
if e_ident[4] != ELFCLASS64:
|
||
raise ValueError("Не 64-битный ELF")
|
||
if e_ident[5] != ELFDATA2LSB:
|
||
raise ValueError("Не little-endian (ожидался x86-64)")
|
||
|
||
return {
|
||
'e_ident': e_ident,
|
||
'e_type': e_type,
|
||
'e_machine': e_machine,
|
||
'e_version': e_version,
|
||
'e_entry': e_entry,
|
||
'e_phoff': e_phoff,
|
||
'e_shoff': e_shoff,
|
||
'e_flags': e_flags,
|
||
'e_ehsize': e_ehsize,
|
||
'e_phentsize': e_phentsize,
|
||
'e_phnum': e_phnum,
|
||
'e_shentsize': e_shentsize,
|
||
'e_shnum': e_shnum,
|
||
'e_shstrndx': e_shstrndx
|
||
}
|
||
|
||
|
||
def read_section_headers(f, shoff, shnum):
|
||
"""Возвращает список словарей с заголовками секций."""
|
||
f.seek(shoff)
|
||
sections = []
|
||
fmt = 'I I Q Q Q Q I I Q Q'
|
||
for _ in range(shnum):
|
||
data = f.read(64)
|
||
vals = struct.unpack(fmt, data)
|
||
sections.append({
|
||
'sh_name': vals[0],
|
||
'sh_type': vals[1],
|
||
'sh_flags': vals[2],
|
||
'sh_addr': vals[3],
|
||
'sh_offset': vals[4],
|
||
'sh_size': vals[5],
|
||
'sh_link': vals[6],
|
||
'sh_info': vals[7],
|
||
'sh_addralign': vals[8],
|
||
'sh_entsize': vals[9]
|
||
})
|
||
return sections
|
||
|
||
|
||
def read_program_headers(f, phoff, phnum):
|
||
"""Возвращает список словарей с программными заголовками."""
|
||
f.seek(phoff)
|
||
headers = []
|
||
fmt = 'I I Q Q Q Q Q Q'
|
||
for _ in range(phnum):
|
||
data = f.read(56)
|
||
vals = struct.unpack(fmt, data)
|
||
headers.append({
|
||
'p_type': vals[0],
|
||
'p_flags': vals[1],
|
||
'p_offset': vals[2],
|
||
'p_vaddr': vals[3],
|
||
'p_paddr': vals[4],
|
||
'p_filesz': vals[5],
|
||
'p_memsz': vals[6],
|
||
'p_align': vals[7]
|
||
})
|
||
return headers
|
||
|
||
|
||
def get_section_name(f, shstrtab_offset, shstrtab_size, sh_name_offset):
|
||
"""Извлекает имя секции из таблицы строк."""
|
||
f.seek(shstrtab_offset + sh_name_offset)
|
||
name = b''
|
||
while True:
|
||
ch = f.read(1)
|
||
if ch == b'\0' or ch == b'':
|
||
break
|
||
name += ch
|
||
return name.decode('latin-1', errors='replace')
|
||
|
||
|
||
def get_flags_description(sh_flags, sh_type=0, sh_info=0):
|
||
"""
|
||
Расшифровка флагов секции: W (запись), A (назнач), X (исполняемый), I (инфо).
|
||
|
||
Для секций REL/RELA флаг 'I' устанавливается, если sh_info указывает
|
||
на целевую секцию (sh_info > 0), как это делает readelf.
|
||
"""
|
||
flags = []
|
||
if sh_flags & 0x1: flags.append('W') # WRITE
|
||
if sh_flags & 0x2: flags.append('A') # ALLOC
|
||
if sh_flags & 0x4: flags.append('X') # EXECINSTR
|
||
if sh_flags & 0x8: flags.append('I') # INFO (GNU extension)
|
||
# Дополнительно: REL/RELA секции с sh_info > 0 получают флаг I
|
||
if (sh_type == SHT_REL or sh_type == SHT_RELA) and sh_info > 0:
|
||
if 'I' not in flags:
|
||
flags.append('I')
|
||
return ''.join(flags)
|
||
|
||
|
||
def is_section_in_segment(sec, ph):
|
||
"""
|
||
Проверяет, принадлежит ли секция сегменту.
|
||
Исключает секции с нулевым адресом (не загружаемые в память).
|
||
"""
|
||
if sec['sh_size'] == 0:
|
||
return False
|
||
if sec['sh_addr'] == 0:
|
||
return False
|
||
if ph['p_memsz'] == 0:
|
||
return False
|
||
# Проверяем, что секция находится внутри сегмента
|
||
return (sec['sh_addr'] >= ph['p_vaddr'] and
|
||
sec['sh_addr'] + sec['sh_size'] <= ph['p_vaddr'] + ph['p_memsz'])
|
||
|
||
|
||
# ---------- функции форматирования (на русском) ----------
|
||
def format_elf_header(hdr):
|
||
"""Возвращает строку с ELF-заголовком в стиле readelf -h."""
|
||
lines = []
|
||
lines.append("Заголовок ELF:")
|
||
lines.append(f" Magic: {hdr['e_ident'].hex(' ')}")
|
||
lines.append(f" Класс: ELF64")
|
||
|
||
if hdr['e_ident'][5] == 1:
|
||
lines.append(f" Данные: дополнение до 2, от младшего к старшему")
|
||
else:
|
||
lines.append(f" Данные: дополнение до 2, от старшего к младшему")
|
||
|
||
lines.append(f" Version: {hdr['e_ident'][6]} (current)")
|
||
lines.append(f" OS/ABI: {OSABI_NAMES.get(hdr['e_ident'][7], 'Неизвестно')}")
|
||
lines.append(f" Версия ABI: {hdr['e_ident'][8]}")
|
||
|
||
etype = ETYPE_NAMES.get(hdr['e_type'], 'UNKNOWN')
|
||
lines.append(f" Тип: {etype}")
|
||
|
||
if hdr['e_machine'] == EM_X86_64:
|
||
lines.append(f" Машина: Advanced Micro Devices X86-64")
|
||
else:
|
||
lines.append(f" Машина: 0x{hdr['e_machine']:x}")
|
||
|
||
lines.append(f" Версия: 0x{hdr['e_version']:x}")
|
||
lines.append(f" Адрес точки входа: 0x{hdr['e_entry']:x}")
|
||
lines.append(f" Начало заголовков программы: {hdr['e_phoff']} (байт в файле)")
|
||
lines.append(f" Начало заголовков раздела: {hdr['e_shoff']} (байт в файле)")
|
||
lines.append(f" Флаги: 0x{hdr['e_flags']:x}")
|
||
lines.append(f" Size of this header: {hdr['e_ehsize']} (bytes)")
|
||
lines.append(f" Size of program headers: {hdr['e_phentsize']} (bytes)")
|
||
lines.append(f" Number of program headers: {hdr['e_phnum']}")
|
||
lines.append(f" Size of section headers: {hdr['e_shentsize']} (bytes)")
|
||
lines.append(f" Number of section headers: {hdr['e_shnum']}")
|
||
lines.append(f" Section header string table index: {hdr['e_shstrndx']}")
|
||
return '\n'.join(lines)
|
||
|
||
|
||
def format_sections(f, sections, shstrtab_offset, shstrtab_size):
|
||
"""Возвращает строку с таблицей секций в стиле readelf -S."""
|
||
lines = []
|
||
lines.append("\nЗаголовки разделов:")
|
||
lines.append(" [Нм] Имя Тип Адрес Смещение Размер Разм.Ent Флаги Ссылк Инфо Выравн")
|
||
|
||
for idx, sec in enumerate(sections):
|
||
name = get_section_name(f, shstrtab_offset, shstrtab_size, sec['sh_name'])
|
||
stype = SHT_NAMES.get(sec['sh_type'], f"0x{sec['sh_type']:x}")
|
||
flags = get_flags_description(sec['sh_flags'], sec['sh_type'], sec['sh_info'])
|
||
|
||
if idx == 0:
|
||
# Пустая секция - специальный формат
|
||
lines.append(f" [{idx:2d}] {name:20s} {stype:16s} {sec['sh_addr']:016x} {sec['sh_offset']:08x}")
|
||
lines.append(f" {sec['sh_size']:016x} {sec['sh_entsize']:016x} {sec['sh_link']:<6d}{sec['sh_info']:<6d}{sec['sh_addralign']:<8d}")
|
||
else:
|
||
flags_str = flags if flags else " "
|
||
lines.append(f" [{idx:2d}] {name:20s} {stype:16s} {sec['sh_addr']:016x} {sec['sh_offset']:08x}")
|
||
lines.append(f" {sec['sh_size']:016x} {sec['sh_entsize']:016x} {flags_str:4s} {sec['sh_link']:<6d}{sec['sh_info']:<6d}{sec['sh_addralign']:<8d}")
|
||
|
||
# Добавляем легенду флагов
|
||
lines.append("Обозначения флагов:")
|
||
lines.append(" W (запись), A (назнач), X (исполняемый), M (слияние), S (строки),")
|
||
lines.append(" I (инфо), L (порядок ссылок), O (требуется дополнительная работа ОС),")
|
||
lines.append(" G (группа), T (TLS), C (сжат), x (неизвестно), o (специфич. для ОС),")
|
||
lines.append(" E (исключён), D (mbind), l (большой), p (processor specific)")
|
||
|
||
return '\n'.join(lines)
|
||
|
||
|
||
def format_program_headers(phdrs):
|
||
"""Возвращает строку с программными заголовками в стиле readelf -l."""
|
||
lines = []
|
||
lines.append("\nЗаголовки программы:")
|
||
lines.append(" Тип Смещ. Вирт.адр Физ.адр")
|
||
lines.append(" Рзм.фйл Рзм.пм Флаги Выравн")
|
||
|
||
for ph in phdrs:
|
||
ptype = PT_NAMES.get(ph['p_type'], f"0x{ph['p_type']:x}")
|
||
|
||
# Формируем флаги
|
||
flags = ''
|
||
if ph['p_flags'] & 4: flags += 'R'
|
||
if ph['p_flags'] & 2: flags += 'W'
|
||
if ph['p_flags'] & 1: flags += 'E'
|
||
if not flags: flags = ' '
|
||
|
||
lines.append(f" {ptype:14s} 0x{ph['p_offset']:016x} 0x{ph['p_vaddr']:016x} 0x{ph['p_paddr']:016x}")
|
||
lines.append(f" 0x{ph['p_filesz']:016x} 0x{ph['p_memsz']:016x} {flags:4s} 0x{ph['p_align']:x}")
|
||
|
||
return '\n'.join(lines)
|
||
|
||
|
||
def format_segment_section_map(sections, phdrs, get_name_func):
|
||
"""Форматирует соответствие сегментов и секций."""
|
||
lines = []
|
||
lines.append("\n Соответствие раздел-сегмент:")
|
||
lines.append(" Сегмент Разделы...")
|
||
|
||
for pidx, ph in enumerate(phdrs):
|
||
seg_sections = []
|
||
for sidx, sec in enumerate(sections):
|
||
if is_section_in_segment(sec, ph):
|
||
name = get_name_func(sidx)
|
||
if name:
|
||
seg_sections.append(name)
|
||
|
||
if seg_sections:
|
||
section_names = ' '.join(seg_sections)
|
||
lines.append(f" {pidx:02d} {section_names}")
|
||
else:
|
||
lines.append(f" {pidx:02d} ")
|
||
|
||
return '\n'.join(lines)
|
||
|
||
|
||
# ---------- главная функция ----------
|
||
def main():
|
||
if len(sys.argv) != 2:
|
||
print(f"Использование: {sys.argv[0]} <elf-файл>", flush=True)
|
||
sys.exit(1)
|
||
|
||
filename = sys.argv[1]
|
||
|
||
try:
|
||
with open(filename, 'rb') as f:
|
||
# 1. ELF заголовок
|
||
hdr = read_elf_header(f)
|
||
if hdr['e_machine'] != EM_X86_64:
|
||
print("Предупреждение: обнаружена не x86-64 архитектура, вывод может быть некорректным.", flush=True)
|
||
print(format_elf_header(hdr), flush=True)
|
||
|
||
# Читаем секции для использования в других блоках
|
||
sections = []
|
||
if hdr['e_shnum'] > 0:
|
||
sections = read_section_headers(f, hdr['e_shoff'], hdr['e_shnum'])
|
||
|
||
# Функция для получения имён секций (для маппинга)
|
||
def get_sec_name(sidx):
|
||
if sidx < len(sections):
|
||
shstrtab = sections[hdr['e_shstrndx']]
|
||
return get_section_name(f, shstrtab['sh_offset'], shstrtab['sh_size'], sections[sidx]['sh_name'])
|
||
return '???'
|
||
|
||
# Вывод секций
|
||
if hdr['e_shnum'] > 0:
|
||
shstrtab = sections[hdr['e_shstrndx']]
|
||
print(format_sections(f, sections, shstrtab['sh_offset'], shstrtab['sh_size']), flush=True)
|
||
else:
|
||
print("\nФайл не содержит таблицы разделов.", flush=True)
|
||
|
||
# Вывод программных заголовков
|
||
if hdr['e_phnum'] > 0:
|
||
phdrs = read_program_headers(f, hdr['e_phoff'], hdr['e_phnum'])
|
||
print(format_program_headers(phdrs), flush=True)
|
||
|
||
# Соответствие сегментов и секций
|
||
print(format_segment_section_map(sections, phdrs, get_sec_name), flush=True)
|
||
else:
|
||
print("\nФайл не содержит программных заголовков.", flush=True)
|
||
|
||
except Exception as e:
|
||
print(f"Ошибка: {e}", file=sys.stderr, flush=True)
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main() |