
Плагин для визуализации покрытия двоичного кода для Ghidra
Dragon Dance — это плагин для Ghidra для визуализации и управления данными покрытия бинарного кода. Данные покрытия могут быть импортированы из нескольких источников покрытия. На данный момент плагин поддерживает инструменты бинарной инструментации Dynamorio и Intel Pin. Dynamorio имеет собственный модуль сбора покрытия под названием "drcov". Intel Pin не предоставляет встроенного модуля сбора покрытия. Чтобы исправить отсутствие модуля, мне пришлось написать собственный модуль сбора покрытия для Intel Pin. Поэтому я написал модуль сбора покрытия для Intel Pin под названием ddph (Dragon Dance Pin Helper). Вы можете его использовать. Исходный код ddph можно посмотреть по этой ссылке. Если вам лень компилировать самостоятельно, вы можете использовать скомпилированные бинарные файлы, которые я предоставил для Windows, macOS и Linux.

Dragon Dance может импортировать и использовать несколько данных покрытия в одном сеансе. (Также поддерживается мультисессионность, но на данный момент это не доступно через GUI). Вы можете переключаться между ними или быстро применять операции пересечения, разности, исключения или суммы.
Dragon Dance позволяет просматривать интенсивность выполненных инструкций. Так вы можете получить подсказку, какие инструкции как часто выполняются. Также вы можете просматривать визуализацию покрытия в окне графа функций.

Dragon Dance также поддерживает собственную систему скриптинга.

Она предоставляет гибкий способ работы с данными покрытия. Вы можете выполнять операции загрузки, удаления, отображения, пересечения, разности, исключения, суммы. Следующий раздел будет содержать систему скриптинга и API. Нажмите клавиши Alt + Enter для выполнения скрипта.
Встроенные функции — это реализация внутренних операций покрытия, предоставляющая интерфейс для системы скриптинга. Встроенная функция может возвращать переменную объекта покрытия или ничего. Встроенные функции могут иметь псевдонимы. Они принимают Built-in Arg в качестве параметра. Параметры могут быть переменной длины.
Built-in Arg — это ссылка для хранения значений разных типов. Built-in Args передаются слева направо. Built-in Arg может хранить следующие типы значений:
Переменные отвечают только за хранение объекта покрытия. Они могут быть загружены встроенными функциями. Они могут передаваться как параметр (Built-in Arg) встроенным функциям.
Существует два типа объекта покрытия.
Physical Coverage Object и Logical Coverage Object
Physical Coverage Object указывает на объект покрытия, загруженный напрямую из файла покрытия. Они видны в таблице покрытия в GUI. Таким образом, вы можете взаимодействовать с ними через операции GUI.
Logical Coverage Object указывает на объект покрытия, который был обработан во встроенной функции и возвращен из нее в качестве результата. Они не видны в GUI, но могут существовать в переменной до тех пор, пока не будут уничтожены.
Объект покрытия автоматически поддерживается объектом Variable для обоих типов объекта покрытия. Например;``` cov1 = load("firstcoverage.out") cov2 = load("secondcov.out")
cov1 = diff(cov1,cov2)
В этом примере cov1 и cov2 являются переменными. И обе переменные имеют объект физического покрытия. Встроенная функция diff принимает обе переменные и устанавливает возвращаемое значение в переменную cov1. Эта операция перезаписи установит результирующий объект покрытия в переменную, но не удалит объект покрытия, так как это физический объект покрытия. Эти данные покрытия останутся в сеансе, а также в таблице GUI.
Давайте рассмотрим предыдущий пример так;```
cov1 = load("first.out")
cov2 = load("second.out")
cov3 = load("third.out")
rvar = sum(cov1,cov2,cov3)
rvar = diff(rvar, cov2)
В этом примере три физические переменные покрытия поступают на операцию суммы, и операция суммы возвращает логический объект покрытия. Затем операция разности принимает логическую и физическую переменную и перезаписывает переменную с именем rvar.
В этом случае результат будет установлен в rvar, а его предыдущее значение покрытия будет немедленно уничтожено. Поскольку это был логический объект, его следует удалить во избежание утечки объектов. Если вы хотите уничтожить переменную, содержащую физический объект покрытия, вам необходимо вызвать встроенную функцию discard. Все встроенные функции будут подробно описаны ниже.
Вы можете писать сложные сценарии, используя вложенные вызовы встроенных функций, например:
cres = diff(intersect(a, load("another.log"), c, d), sum(e,f) )
вам не нужно писать логику строка за строкой.
Следующая документация API и их поведение могут измениться до выхода финальной версии.
clear()
| Свойство | Описание |
|---|---|
| Возвращаемое значение | None |
| Минимальное количество параметров | 0 |
cwd( String : workingDirectory )
diff( Variable : var1, var2, ..... varN )
| Свойство | Описание |
|---|---|
| Возвращаемое значение | Variable |
| Минимальное количество параметров |
discard( Variable : var1, var2, ..... varN )
| Свойство | Описание |
|---|---|
| Возвращаемое значение |
distinct( Variable : var1, var2, ..... varN )
| Свойство | Описание |
|---|---|
| Возвращаемое значение | Variable |
| Минимальное количество параметров |
goto( Integer : offset )
| Свойство | Описание |
|---|---|
| Возвращаемое значение | None |
| Минимальное количество параметров |
import( String : filePathOrCoverageName )
intersect( Variable : var1, var2, ..... varN )
| Свойство | Описание |
|---|---|
| Возвращаемое значение | Variable |
| Минимальное количество параметров |
show( Variable : var )
| Свойство | Описание |
|---|---|
| Возвращаемое значение | None |
sum( Variable : var1, var2, ..... varN )
| Свойство | Описание |
|---|---|
| Возвращаемое значение | Variable |
| Минимальное количество параметров |
Исправления
Dragon Dance может попытаться исправить неверно проанализированную ситуацию в Ghidra во время импорта файла данных покрытия. В некоторых бинарных файлах Ghidra не декомпилирует инструкции функции из-за неожиданной генерации кода компилятором. Dragon Dance проверяет загруженный образ и целостность данных покрытия. Если они соответствуют друг другу, и адрес принадлежит исполняемой секции, но отсутствует декомпиляция инструкций, плагин предлагает исправить. Затем он пытается исправить, декомпилируя сырую секцию.

В будущих версиях плагина могут появиться дополнительные исправления или обходные пути для образа.
Установка довольно проста.
Запустите Ghidra.
Нажмите меню "File", затем выберите "Install Extensions..."
Нажмите зелёный значок плюса в правом верхнем углу окна.
Выберите zip-пакет плагина и нажмите Ok.
Выберите dragondance из списка.
Нажмите Ok и перезапустите Ghidra.
При первой загрузке бинарного файла в Ghidra после установки плагина Ghidra спросит, хотите ли вы настроить только что установленный плагин.
Если вы нажмёте кнопку Да, плагин DragonDance должен появиться немедленно.
Если вы нажмёте кнопку Нет, вам придётся активировать его вручную.
Для ручной активации:
Нажмите меню "File", затем в окне дизассемблирования (CodeBrowser) выберите "Configure".
Нажмите маленький значок вилки в правом верхнем углу окна "Configure Tool".
Найдите элемент DragonDance в списке плагинов, отметьте его флажок и нажмите Ok.
Должно появиться окно Dragon Dance.
После активации вы должны увидеть пункт Dragon Dance в меню "Window".
Как я описывал ранее, Dragon Dance может импортировать данные покрытия из Dynamorio и Intel Pin (пока). На самом деле это универсальные инструменты бинарной инструментации. Вам нужно использовать соответствующий модуль для сбора данных покрытия. У Dynamorio есть собственный модуль покрытия с названием drcov. Вы можете использовать этот встроенный модуль для сбора покрытия.
Использование Dynamorio
Вы можете собрать данные покрытия с помощью Dynamorio следующей командой:
drrun -t drcov -logdir [ПУТЬ_К_ДИРЕКТОРИИ_ВЫВОДА_ПОКРЫТИЯ] -- [ПУТЬ_К_ИССЛЕДУЕМОМУ_ИСПОЛНЯЕМОМУ_ФАЙЛУ] [АРГУМЕНТЫ_ИСПОЛНЯЕМОГО_ФАЙЛА]
Вывод будет помещён в указанную директорию в формате drcov.[ИМЯ_ИСПОЛНЯЕМОГО_ФАЙЛА].[ID].proc.log.
Использование Intel Pin
Как я упоминал ранее, Intel Pin не предоставляет встроенного модуля для сбора покрытия. Вам необходимо использовать пользовательский модуль для Pin. К счастью, я создал свой собственный для сбора покрытия с помощью Pin. Это даёт нам несколько преимуществ. Я могу расширять его при необходимости или добавлять дополнительные функции, опции.
Хотя более поздние версии могут работать, поддерживается только Intel PIN 3.7. Они не сразу доступны на странице Intel PIN, поэтому вот прямые ссылки для скачивания:
Вы можете получить доступ к исходному коду ddph здесь. Я предоставлю бинарные файлы для Windows, macOS и Linux. Или вы можете собрать свой собственный бинарный файл, используя скрипт сборки.
Для сбора данных покрытия с помощью Intel Pin используйте следующую команду:
pin -t ddph.[so,dylib,dll] [опции ddph] -- [ПУТЬ_К_ИССЛЕДУЕМОМУ_ИСПОЛНЯЕМОМУ_ФАЙЛУ] [АРГУМЕНТЫ_ИСПОЛНЯЕМОГО_ФАЙЛА]
У ddph есть несколько опций для сбора.
-o: с помощью этой опции вы можете указать имя выходного файла покрытия. (по умолчанию: ddph.out)
-l: вы можете указать имя файла журнала операций. Если передать "no" в эту опцию, ddph не будет выполнять логирование. (по умолчанию: ddph.log)
-p: уровень детализации захвата. Эта опция может быть reduced или high. Высокий уровень захватывает все инструкции по одной и строит блоки предварительной обработки выполнения. Это даст вам более интенсивный вывод покрытия, но медленнее, чем reduced. Reduced уровень использует блоки трассировки Pin, поэтому он намного быстрее, чем высокий уровень. Но это не приведёт к огромным различиям в уровне вывода покрытия. Если вы не собираетесь выполнять специфические задачи, рассмотрите возможность использования reduced уровня. (по умолчанию: reduced)
Для пользователей macOS: Начиная с macOS 10.11 (OS X El Capitan), в ОС появился уровень безопасности под названием System Integrity Protection SIP. Он предотвращает попытки процессов пользовательского режима внедряться или модифицировать другие процессы, даже если вы работаете с правами root.
Чтобы обойти это ограничение, вам необходимо отключить его. Для этого выполните следующие шаги.
Перезагрузите macOS.
Во время загрузки нажмите и удерживайте клавиши Command + R.
ОС в конечном итоге войдёт в режим восстановления.
Откройте терминал из раздела "Утилиты".
Введите csrutil status и нажмите ввод. Вы должны увидеть, что SIP включён.
Введите csrutil disable и нажмите ввод.
Снова введите csrutil status, чтобы убедиться, что он отключён. Затем перезагрузите ОС и дайте ей загрузиться в обычном режиме. Теперь вы готовы к использованию инструментов бинарной инструментации.



Сначала загрузите ghidra (последняя версия, на данный момент 9.1.2) и dragondance.``` $ wget https://ghidra-sre.org/ghidra_9.1.2_PUBLIC_20200212.zip $ wget https://github.com/0ffffffffh/dragondance/archive/master.zip $ unzip ghidra_9.1.2_PUBLIC_20200212.zip $ unzip master.zip
Далее установите gradle и jdk```
$ sudo apt install openjdk-11-jdk
$ wget https://services.gradle.org/distributions/gradle-5.2.1-bin.zip
$ sudo unzip -d /opt/gradle gradle-5.2.1-bin.zip
создать новый файл профиля``` $ sudo vi /etc/profile.d/gradle.sh
и добавьте следующее, чтобы добавить gradle в PATH при каждом последующем входе.```
export GRADLE_HOME=/opt/gradle/gradle-5.2.1
export PATH=${GRADLE_HOME}/bin:${PATH}
сделать это немедленно, без необходимости выходить из системы``` $ source /etc/profile.d/gradle.sh
теперь мы можем приступить к сборке dragondance```
$ cd dragondance-master/
$ gradle -PGHIDRA_INSTALL_DIR=/home/ubuntu/ghidra_9.1.2_PUBLIC
> Task :buildExtension
Created ghidra_9.1.2_PUBLIC_20200506_dragondance-master.zip in /home/ubuntu/dragondance-master/dist
BUILD SUCCESSFUL in 29s
5 actionable tasks: 5 executed
ubuntu@ubuntu:~/dragondance-master$
где вам может понадобиться настроить путь к каталогу, в который вы скачали Ghidra. Теперь вы можете найти собранное расширение в dragondance-master/dist
Выполнение скриптов построчно в стиле командной строки.
Отслеживание изменений контекста
Визуализация покрытия на основе функций (подпрограмм)
Отслеживание потока выполнения
Больше встроенных функций для скриптования
Собственный формат базы данных покрытия для более быстрого сохранения и загрузки, а также для сохранения последних изменений в сессии.
Раскраска псевдокода. (Ghidra не предоставляет API для этого. Поэтому мне нужно изучить исходный код Ghidra, чтобы найти способ или обходной путь для его реализации.)
Улучшения пользовательского интерфейса
Oğuz Kartal (@0ffffffffh)
| Максимальное количество параметров |
| 0 |
| Описание | Эта встроенная функция очищает текущее отображаемое покрытие и устанавливает активное покрытие в null. |
| Псевдонимы | None |
| Свойство | Описание |
|---|
| Возвращаемое значение | None |
| Минимальное количество параметров | 1 |
| Максимальное количество параметров | 1 |
| Описание | Устанавливает текущую рабочую директорию по заданному пути. Все вызовы import без абсолютного пути после cwd будут искать файлы покрытия в текущей рабочей директории. |
| Псевдонимы | None |
| 2 |
| Максимальное количество параметров | Неограниченно |
| Описание | Применяет операцию разности к заданному набору переменных переменной длины. И возвращает результирующую переменную покрытия. |
| Псевдонимы | None |
| None |
| Минимальное количество параметров | 1 |
| Максимальное количество параметров | Неограниченно |
| Описание | Уничтожает переменные, как физические, так и логические. Сначала уничтожает объект покрытия, а затем удаляет имя переменной из списка переменных. После этого вызова все заданные переменные становятся неопределёнными. |
| Псевдонимы | del |
| 2 |
| Максимальное количество параметров | Неограниченно |
| Описание | Применяет операцию исключающего ИЛИ (xor) к заданному набору переменных переменной длины. И возвращает результирующую переменную покрытия. |
| Псевдонимы | xor |
| 1 |
| Максимальное количество параметров | 1 |
| Описание | Перемещает текущий выбор адреса на заданное смещение. Реальное значение адреса вычисляется путём добавления смещения к базовому адресу образа. |
| Псевдонимы | None |
| Свойство | Описание |
|---|
| Возвращаемое значение | Variable |
| Минимальное количество параметров | 1 |
| Максимальное количество параметров | 1 |
| Описание | Импортирует данные покрытия из физического файла покрытия. Принимает относительный или абсолютный путь. Или имя покрытия, загруженного ранее физически. Если заданный путь является абсолютным, import загружает напрямую из этого пути. В противном случае он ищет файл в текущей рабочей директории. В обоих случаях import проверяет, были ли данные покрытия уже загружены по этому пути. Если уже загружено, возвращает кэшированную переменную покрытия. Или, если задано имя физического покрытия, он ищет карту покрытия в своей сессии и возвращает объект покрытия, если он существует. |
| Псевдонимы | get, load |
| 2 |
| Максимальное количество параметров | Неограниченно |
| Описание | Применяет операцию пересечения к заданному набору переменных переменной длины. И возвращает результирующую переменную покрытия. |
| Псевдонимы | and |
| Минимальное количество параметров | 1 |
| Максимальное количество параметров | 1 |
| Описание | Визуализирует заданную переменную покрытия. Если есть активный визуализированный объект покрытия и он является логическим, функция немедленно уничтожает предыдущий объект покрытия и показывает заданный. |
| Псевдонимы | None |
| 1 |
| Максимальное количество параметров | Неограниченно |
| Описание | Применяет операцию суммы к заданному набору переменных переменной длины. И возвращает результирующую переменную покрытия. |
| Псевдонимы | or, union |