#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Скрипт генерирует 64-битный исполняемый ELF-файл для Linux (x86-64), # содержащий 12 инструкций NOP (0x90) в секции .text и один байт (0x0A) # в секции .data. Файл полностью корректен и распознаётся утилитами # objdump, readelf и другими стандартными инструментами. import struct def pack_elf(): """ Создаёт байтовое представление валидного ELF-исполняемого файла. Возвращает bytes — содержимое файла. """ # Размер страницы памяти в Linux на x86-64 (4096 байт = 0x1000) PAGE_SIZE = 0x1000 # Адрес точки входа программы — начало секции .text ENTRY = 0x401000 # === СОДЕРЖИМОЕ СЕКЦИЙ === # Машинный код: 12 инструкций NOP (код операции 0x90) text_code = b'\x90' * 12 # Данные: один байт — символ новой строки (0x0A = '\n') data_content = b'\x0a' # Таблица строк имён секций (обязательна для Section Headers) # Формат: нуль-терминированные строки подряд. # Порядок: "", ".text", ".data", ".shstrtab" shstrtab_content = b'\x00.text\x00.data\x00.shstrtab\x00' # === СМЕЩЕНИЯ В ФАЙЛЕ (в байтах) === # Program Headers (заголовки сегментов) начинаются сразу после ELF-заголовка phdr_start = 64 # размер ELF-заголовка # Секция .text начинается с границы страницы 0x1000 (4096) text_offset = 0x1000 # Секция .data начинается со следующей страницы — 0x2000 (8192) data_offset = 0x2000 # Таблица имён секций (.shstrtab) размещается сразу после .data shstrtab_offset = data_offset + len(data_content) # 0x2000 + 1 = 0x2001 # Заголовки секций (Section Headers) размещаются сразу после .shstrtab shdr_offset = shstrtab_offset + len(shstrtab_content) # === РАЗМЕРЫ КОМПОНЕНТОВ === text_size = len(text_code) # 12 байт data_size = len(data_content) # 1 байт shstrtab_size = len(shstrtab_content) # длина строки имён # Размер одного заголовка секции — 64 байта для 64-битного ELF # Всего 4 секции: NULL, .text, .data, .shstrtab shdr_size = 4 * 64 # Общий размер файла: от начала до конца последнего заголовка секции file_size = shdr_offset + shdr_size # Создаём байтовый массив нужного размера, заполненный нулями elf_data = bytearray(file_size) # === 1. ELF-ЗАГОЛОВОК (64 байта) === # e_ident — магическое число и базовые параметры формата e_ident = ( b'\x7fELF' # Магическое число ELF b'\x02' # 64-битный файл b'\x01' # Little-endian (младший байт первым) b'\x01' # Версия ELF (1) b'\x00' * 9 # Остальные поля e_ident — нули (OSABI=System V и т.д.) ) # Упаковываем ELF-заголовок в бинарный формат # Формат: < — little-endian; 16s — 16 байт; H — uint16; I — uint32; Q — uint64 elf_header = struct.pack( '<16sHHIQQQIHHHHHH', e_ident, # e_ident[16] 2, # e_type = ET_EXEC (исполняемый файл) 0x3e, # e_machine = EM_X86_64 (AMD x86-64) 1, # e_version = 1 ENTRY, # e_entry — адрес точки входа phdr_start, # e_phoff — смещение Program Headers в файле shdr_offset, # e_shoff — смещение Section Headers в файле 0, # e_flags — флаги архитектуры (0 для x86-64) 64, # e_ehsize — размер ELF-заголовка (64 байта) 56, # e_phentsize — размер одного Program Header (56 байт) 3, # e_phnum — количество Program Headers 64, # e_shentsize — размер одного Section Header (64 байта) 4, # e_shnum — количество Section Headers 3 # e_shstrndx — индекс секции с именами (.shstrtab) ) # Записываем ELF-заголовок в начало файла elf_data[0:64] = elf_header # === 2. PROGRAM HEADERS (заголовки сегментов) === # Функция для создания одного Program Header (PT_LOAD) def make_phdr(p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align): """ Создаёт бинарное представление заголовка сегмента. Поля: p_type — тип сегмента (1 = PT_LOAD) p_flags — права доступа (PF_R=4, PF_W=2, PF_X=1) p_offset — смещение сегмента в файле p_vaddr — виртуальный адрес загрузки в памяти p_paddr — физический адрес (в Linux не используется, = vaddr) p_filesz — размер данных в файле p_memsz — размер в памяти (может быть больше для .bss) p_align — выравнивание (обычно PAGE_SIZE) """ return struct.pack('