Пиков / КАПО
Как читать авторский курс и нормативные материалы

Это учебные материалы, а не заключение о соответствии конкретного продукта. Организационные модели курса отделяйте от терминов стандарта: ППК в опубликованном проекте — машиночитаемый перечень, а живой реестр его версий является практикой сопровождения. Термин OSA в разных материалах имеет разный охват. Статус и обязательность нормативного документа проверяются отдельно; проект КАПО опубликован как проект первой редакции.

ldd readelf objdump

Почему это важно для SCA/OSA

Три классические утилиты бинарного анализа отвечают на разные вопросы, и путать их — значит получать неверный состав. ldd отвечает «что фактически загрузится на этом хосте», readelf — «что записано в самом файле», objdump — «как устроены секции и код». Для инвентаризации это не взаимозаменяемые инструменты, а разные срезы: один показывает рантайм с учётом окружения, другой — статичную запись, третий — детали уровня дизассемблера.

Выбор инструмента влияет и на достоверность, и на безопасность. Опираться на ldd там, где нужен readelf, опасно: ldd зависит от текущих LD_LIBRARY_PATH/RPATH и — что важнее — фактически задействует динамический загрузчик целевого бинаря. На чужом, недоверенном артефакте это уже не пассивное чтение.

Как это проявляется

Один и тот же бинарь, три инструмента, три разных ответа:

# ldd — резолвинг на ЭТОМ хосте: имя -> фактический файл (зависит от окружения)
$ ldd ./app
        libssl.so.3 => /usr/lib/x86_64-linux-gnu/libssl.so.3 (0x00007f...)
        libc.so.6   => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)

# readelf — что записано в файле, без запуска и без окружения
$ readelf -d ./app | grep NEEDED
 0x0...0001 (NEEDED) Shared library: [libssl.so.3]

# objdump — секции/заголовки; полезно для формата, опций сборки, отладочной инфы
$ objdump -p ./app | grep -E 'NEEDED|RUNPATH'
  NEEDED               libssl.so.3
  RUNPATH              $ORIGIN/../lib

ldd дал путь (но он верен только для этого хоста и окружения), readelf дал имя без пути (зато детерминированно и для любой архитектуры), objdump -p показал и имена, и пути поиска. Для воспроизводимой инвентаризации источник истины — readelf/objdump, а ldd — уточнение «куда оно реально резолвится здесь».

ldd запускает загрузчик бинаря ldd работает, по сути, вызывая динамический загрузчик целевого файла, чтобы тот разрешил зависимости. На недоверенном бинаре это нежелательно (и исторически приводило к выполнению кода). Для инвентаризации недоверенных артефактов используйте readelf -d / objdump -p — они только читают файл. ldd оставляйте для своих сборок, когда нужен фактический путь.

Слепая зона или приём

Общая слепая зона всех трёх — они видят только то, что попало в ELF динамически или в символы. Статически вшитый код (.a) ни ldd, ни readelf -d, ни objdump -p не покажут как зависимость — это зона статической линковки. А ldd вдобавок зависит от хоста: на машине с другим RPATH/LD_LIBRARY_PATH он выдаст другие пути, чем целевая среда.

Приём — комбинировать по роли: readelf -d как детерминированный список (DT_NEEDED, структура разобрана в ELF и разделы dynamic), objdump для деталей формата и опций сборки, ldd — только чтобы подтвердить фактический путь на доверенном хосте. Для одиночного бинаря этого набора достаточно; для пачки артефактов ручные утилиты неудобны — там нужен программный парсер (см. LIEF как программный анализ ELF).

Что делать на проекте

  1. Источником списка динамических зависимостей делать readelf -d ... | grep NEEDED, а не ldd — детерминированно и без исполнения.
  2. ldd применять только к своим, доверенным сборкам и только чтобы уточнить фактический путь резолвинга.
  3. Перед анализом проверять формат: file ./app — ELF ли это и та ли архитектура; иначе вывод утилит обманчив.
  4. objdump -p использовать, чтобы за один вызов увидеть и NEEDED, и RPATH/RUNPATH; objdump -h/readelf -S — чтобы понять, stripped ли бинарь (см. stripped binaries).
  5. Помнить, что ldd на аудиторском хосте может дать пути, отличные от целевой среды, — версии подтверждать через пакетный менеджер, а не через путь ldd.
  6. Для массового разбора (десятки бинарей) переходить с ручных команд на скрипт поверх ELF-парсера.

Связь