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

Анализ зависимостей сайта execinfo.ru
Анализ зависимостей сайта execinfo.ru

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

Так-же, в случае если Common библиотеки собираются в отдельном решении и используются в большом количестве других решений, то может быть полезно узнать, а какие публичные объекты из Common библиотек используются в других проектах? Или часть из этих публичных объектов уже не используется и их можно спокойно удалять или менять уровень видимости?

Отрисовка графиков всех зависимостей

В этот момент я подумал что неплохо-бы нарисовать полный график всех зависимых сборок чтобы понимать, от каких сборок зависят конкретные части логики, а какие сборки, по неизвестным причинам, уже давно не существуют на серверах. С использованием моего компонента PEReader эту идею удалось реализовать не только для CLI сборок, включая неуправляемые библиотеки, которые используются в качестве зависимостей через DllImportAttribute (Они хранятся в таблице метаданных ModuleRef), но и для неуправляемых приложений, которые используют PE Import и Export директории.

Поиск зависимостей используется не только в текущей папке, но и в шаренных папках (GAC, %winDir% и т.п.). Как и для управляемых сборок, так и для неуправляемых PE файлов. Для управляемых сборок используется Fusion для поиска сборок, а для неуправляемых библиотек используется функция SearchPath.

Увы, для .NET Core и .NET найти универсальный алгоритм поиска зависимостей в шаренных папках мне не удалось, поэтому если Вы знаете как это сделать, то буду благодарен за подсказку.

Итак, для примера рассмотрим пример на приложении из 8 исполняемых файлов Flatbed.MDI:

Содержимое папки и исполняемый файл, который мы будем анализировать
Содержимое папки и исполняемый файл, который мы будем анализировать

Подсветка используемая в примерах (Меняется в настройках):

  • Чёрными стрелками подсвечены найденные библиотеки.

  • Красными стрелками подсвечены не найденные библиотеки, но если при этом у библиотеки кремовый задний фон, то библиотека нашлась, но другой версии. Если задний фон у библиотеки белый, то её не удалось найти.

  • Зелёными стрелками подсвечены библиотеки, которые нашлись в шаренных папках.

Результат анализа файлов в папке выше
Результат анализа файлов в папке выше

А теперь добавим все зависимости из GAC для этого приложения:

В дополнение к анализу папки - результат анализа всех управляетмых библиотек в GAC
В дополнение к анализу папки - результат анализа всех управляетмых библиотек в GAC

А так будет выглядеть все ссылки на native библиотеки, без сборок из GAC:

В дополнение к анализу папки - анализ всех неуправляемых библиотек (Список урезан для наглядности)
В дополнение к анализу папки - анализ всех неуправляемых библиотек (Список урезан для наглядности)

Ну и напоследок, посмотрим какие ещё лежат в папке библиотеки, но которые не использует ни один из исполняемых файлов в текущей папке:

Просмотр не только зависимостей от первоначального файла, но и других файлов в текущей папке
Просмотр не только зависимостей от первоначального файла, но и других файлов в текущей папке

В данном случае видно, что библиотеки AspNet.Security.OAuth.Apple.dll и Plugin.FileDomainPluginProvider.dll не используются ни одной из сборок напрямую. Более того, библиотека AspNet.Security.OAuth.Apple.dll зависит от большого количества других библиотек, но ни одна из них не найдена. Из этого можно сделать вывод, что ни одна из этих библиотек не испольуется, однако библиотека Plugin.FileDomainPluginProvider.dll использует общий компонент SAL.Flatbed.dll, так что можно предположить, что данная библиотека используется не напрямую, а косвенно через рефлексию.

Так что видимость что библиотека не используется напрямую, ещё не гарантирует что данная библиотека абсолютно бесполезна и в данном случае придётся разбираться вручную.

Отображение всех используемых публичных объектов

После того как удалось понять как исполняемые файлы зависят между собой, интересно узнать, какие именно публичные функции, методы, свойства, события или поля используются из родительского исполняемого файла. Для этого, выбираем стрелку с зависимостью и по нажатию правой кнопкой мыши на стрелку - выбираем пункт меню Analyze:

Интерфейс зависимостей через Flatbed.MDI
Интерфейс зависимостей через Flatbed.MDI

Так же можно открыть интересующий файл напрямую из мень хоста, дабы миновать процесс построения диаграммы. Проанализировав файл - мы увидим следующую картину с отфильтрованными объектами или со всеми объектами. Где цветом выделено то, что используется, а что не используется:

Все публичные объекты с подсветкой неиспользованных
Все публичные объекты с подсветкой неиспользованных
Список только используемых объектов, без списка неиспользованных
Список только используемых объектов, без списка неиспользованных

Если набросать сборок из других проектов, которые тоже ссылаются на корневую сборку SAL.Flatbed, то список используемых публичных объектов значительно расширится, но всё равно - часть объектов явно нигде не используется:

Серым подсвечены публичные объекты, которые нигде не используются
Серым подсвечены публичные объекты, которые нигде не используются

Однако, для примера при просмотре зависимостей из kernel32.dll, лучше смотреть только используемые функции, а не все подряд:

Список всех экспортируемых функций и список импортируемых функций, которые не удалось найти, но есть упоминание в зависимой библиотеке
Список всех экспортируемых функций и список импортируемых функций, которые не удалось найти, но есть упоминание в зависимой библиотеке

Но стоит учитывать, что константы в управляемых приложениях - переносятся в зависимые сборки в момент компиляции, так что их не удастся увидеть в зависимостях, по причине отсутствия ссылки на объект.

Заключение

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

Внешние ресурсы

  • Dependency Analyzer - Описанное в данной статье приложение.

  • SAL Host MDI - Используемое приложение для анализа (Подробнее).

  • Executable Info - Онлайн анализ исполняемых форматов (PE, APK, Davlik, ELF, CLASS).

  • GitHub PEReader - Исходники компонента, который читает директории и метаданные в PE файлах.

Комментарии (0)