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

Чтение исполняемого файла и листинга

Разработчику

Контекст: исполняемый ELF под Linux x86-64. Инструменты: readelf, objdump, отладчик. Обзор RE — в истории ассемблера.

Дизассемблирование ELF - чтение бинарника

Исходника нет — остаётся дизассемблирование: восстановление мнемоник из байт .text. Даже со своим кодом листинг полезен: увидеть, что NASM и линкер реально положили в файл, где PLT, выравнивание, RIP-relative адреса.


Из чего состоит ELF (упрощённо)

СекцияСодержимое
.textмашинные инструкции
.rodataконстанты (строки, таблицы)
.dataинициализированные глобальные данные
.bssнулевые данные при загрузке (в файле только размер)
.symtab / .dynsymимена функций и переменных
.rela.*как переписать адреса при загрузке (PIC, линковка с libc)

Заголовок ELF описывает архитектуру (x86-64), точку входа (e_entry) и таблицу секций.

readelf -h ./app # заголовок
readelf -S ./app # секции
readelf -s ./app # символы

Дизассемблирование

objdump -d -M intel ./app

-M intel — синтаксис как в NASM (dest, src), а не AT&T.

Фрагмент может выглядеть так:

0000000000401000 <_start>:
401000: b8 01 00 00 00 mov eax,0x1
401005: bf 01 00 00 00 mov edi,0x1
...

Слева — виртуальный адрес после загрузки, справа — шестнадцатеричные байты и мнемоники.

С исходником рядом:

objdump -d -M intel -S ./app # если есть отладочные символы (-g при сборке)

Сопоставление с NASM-исходником

В исходникеВ листинге
mov rax, 1mov rax,0x1 — может быть другая длина инструкции
lea rdi, [rel msg]RIP-relative: lea rdi,[rip+0x...]
call printfчасто call *...@plt — прыжок через таблицу процедур линковки
section .bssв файле секция пустая, размер в заголовке
локальная метка .loopможет отсутствовать в символах; только адреса

Не каждая метка попадает в символьную таблицу — локальные и static в C скрыты.


Таблица символов

nm ./app

Буквы состояния:

  • T / t — код в .text (глобальный / локальный)
  • D / d — инициализированные данные
  • B / b — BSS
  • U — неразрешённый (ожидает libc при линковке)

Имя _start или main — точка входа. Несколько U на printf — программа слинкована с динамической libc.


Отладочная информация

Сборка с символами:

gcc -g main.c add.o -o app

В GDB: disassemble /s my_add, info registers, x/s msg.

Без -g остаются только публичные символы и догадки по вызовам API.


Типичные артефакты, которые путают новичков

  1. NOP-заполнение — выравнивание входа функции на 16 байт.
  2. Две метки на один адрес — оптимизация линкера / альтернативные имена символов.
  3. Адреса 0x7f... — позиционно-независимый код (PIE); при каждом запуске смещение (ASLR).
  4. Строка в .rodata — в листинге не видна; смотрят objdump -s -j .rodata или strings ./app.

Минимальный чек-лист разбора

  1. readelf -h — 64-bit, ELF, entry.
  2. readelf -S — есть .text, .rodata, размеры разумны.
  3. objdump -d -M intel — логика _start / main.
  4. nm — какие функции экспортированы, что тянется из libc.
  5. При расхождении с ожиданием — пересобрать с -g и сравнить в GDB по шагам.

Дальше

Практика отладки — справочник, раздел про GDB. Строковые циклы REPотдельная статья. SIMD и float в листинге — 15.md. Бинарник Windows (PE) — 16.md.


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).