
Инструмент инструментирования на основе LLVM для универсального отслеживания помеченных данных, анализа потоков данных и трассировки.
PolyTracker — это инструмент, изначально созданный для Автоматической лексической аннотации и навигации парсеров (Automated Lexical Annotation and Navigation of Parsers), обратного акронима, придуманного исключительно для того, чтобы называть его The ALAN Parsers Project. Однако со временем он превратился в универсальный инструмент для эффективного выполнения анализа потоков данных и потока управления программ. PolyTracker — это LLVM-проход, который инструментирует программы для отслеживания того, над какими байтами входного файла работают те или иные функции. Он выдаёт базу данных с информацией о потоках данных, а также трассу выполнения. PolyTracker также предоставляет библиотеку Python для взаимодействия со своими выходными данными и их анализа, а также интерактивную REPL-среду Python.
PolyTracker можно использовать вместе с PolyFile для автоматического определения семантического назначения функций в парсере. Также у него есть экспериментальная функция, способная генерировать контекстно-свободную грамматику, описывающую язык, принимаемый парсером.
В отличие от инструментов динамической инструментации, таких как , PolyTracker почти для всех входных данных вносит незначительные накладные расходы на производительность и способен отслеживать каждый байт входа одновременно. PolyTracker начинался как форк LLVM DataFlowSanitizer и черпает много вдохновения из . Однако, в отличие от системы Angora, PolyTracker способен отслеживать полное taint-метки. В феврале 2021 года LLVM DataFlowSanitizer добавил новую функцию для отслеживания происхождения taint-меток, названную . Однако он способен отслеживать не более 16 taint-меток одновременно, тогда как PolyTracker может отслеживать до 2-1.
Этот README служит общим руководством по установке PolyTracker и компиляции/инструментированию двоичных файлов. Для программного взаимодействия с PolyTracker, его расширения через Python API, а также для работы с трассами выполнения, созданными инструментированным кодом, обратитесь к документации Python.
PolyTracker управляется через Python-скрипт polytracker. Вы можете установить его, выполнив
pip3 install polytracker
PolyTracker требует очень специфического системного окружения для работы, поэтому почти всем пользователям, скорее всего, придётся запускать его в контейнерной среде. К счастью, polytracker упрощает эту задачу. Вам достаточно установить docker, а затем выполнить:
polytracker docker pull
и
polytracker docker run
Последняя команда смонтирует текущую рабочую директорию в Docker-контейнер PolyTracker и позволит вам собирать и запускать инструментированные программы.
Управляющий скрипт polytracker — который можно запускать как на хост-системе, так и внутри Docker-контейнера — имеет множество команд как для инструментирования программ, так и для анализа полученных артефактов. Например, вы можете исследовать потоки данных в ходе выполнения, восстановить граф потока управления инструментированной программы и даже извлечь контекстно-свободную грамматику, соответствующую входным данным, принимаемым программой. Вы можете изучить эти команды, выполнив
polytracker --help
Скрипт polytracker также является REPL-средой, если запускать его без аргументов командной строки:
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> commands
PolyTracker также поставляется с командой build. Эта команда позволяет пользователю выполнять любую команду сборки в инструментированной среде Blight. При этом будет создан файл blight_journal.jsonl, в котором записываются все команды, выполненные во время сборки. Если у вас есть цель на C/C++, вы можете инструментировать её, вызвав polytracker build и передав команду сборки:
polytracker build gcc -g -o my_binary my_source.c
Чтобы инструментировать целевой артефакт сборки, используйте команду instrument-targets. По умолчанию команда использует файл blight_journal.jsonl в вашей текущей рабочей директории для создания инструментированной версии целевого артефакта. Инструментированная цель будет собрана с теми же флагами, что и исходная.
polytracker instrument-targets my_binary
build также поддерживает более сложные программы, использующие систему сборки, например autotools или CMake:
polytracker build cmake .. -DCMAKE_BUILD_TYPE=Release
polytracker build ninja
# or
polytracker build ./configure
polytracker build make
Затем запустите instrument-targets для любых целей сборки:
polytracker instrument-targets a.bin b.so
Тогда a.instrumented.bin и b.instrumented.so будут инструментированными версиями. Примеры инструментирования реальных программ см. в Dockerfile в каталоге examples.
Инструментированное ПО будет записывать свои выходные данные в путь, указанный в POLYDB, или в polytracker.tdag, если он не указан. Это двоичный файл, с которым можно работать, выполнив:
from polytracker import PolyTrackerTrace, taint_dag
trace = PolyTrackerTrace.load("polytracker.tdag")
tdfile = trace.tdfile
first_node = list(tdfile.nodes)[0]
print(f"First node affects control flow: {first_node.affects_control_flow}")
# Operate on all Range nodes
for index, node in enumerate(tdfile.nodes):
if isinstance(node, taint_dag.TDRangeNode):
print(f"Node {index}: first {node.first}, last {node.last}")
# Access taint forest
tdforest = trace.taint_forest
n1 = tdforest.get_node(1)
print(
f"Forest node {n1.label}. Parent labels: {n1.parent_labels}, "
f"source: {n1.source.path if n1.source is not None else None}, "
f"affects control flow: {n1.affected_control_flow}"
)
Вы также можете запустить инструментированный двоичный файл непосредственно из REPL:
$ polytracker
PolyTracker (4.0.0)
https://github.com/trailofbits/polytracker
Type "help" or "commands"
>>> trace = run_trace("path_to_binary", "path_to_input_file")
При необходимости это автоматически запустит инструментированный двоичный файл в Docker-контейнере.
⚠️ Если вы запускаете PolyTracker в Docker или виртуальной машине: PolyTracker может работать очень медленно в виртуализированной среде, если входной файл или, особенно, выходная база данных находятся в каталоге, смонтированном с хост-ОС. Это особенно актуально при запуске PolyTracker в Docker на хост-системе macOS. Решение — записывать базу данных в путь внутри контейнера/ВМ и копировать её на хост-систему в самом конце.
Документация Python API доступна здесь.
Во время выполнения инструментирование PolyTracker ищет несколько параметров конфигурации, задаваемых через переменные окружения. Это позволяет изменять параметры инструментирования без необходимости перекомпиляции двоичного файла.
PolyTracker принимает параметры конфигурации в виде переменных окружения, чтобы избежать перекомпиляции целевых программ. Текущий набор переменных окружения, поддерживаемых PolyTracker:
POLYDB: A path to which to save the output database (default is polytracker.tdag)
WLLVM_ARTIFACT_STORE: Provides a path to an existing directory to store artifact/manifest for all build targets
POLYTRACKER_TAINT_ARGV: Set to '1' to use argv as a taint source.
POLYTRACKER_STDIN_SOURCE: Set to '1' to use stdin as a taint source.
POLYTRACKER_STDOUT_SINK: Set to '1' to use stdout as a taint sink.
POLYTRACKER_STDERR_SINK: Set to '1' to use stderr as a taint sink.
Polytracker будет устанавливать свои параметры конфигурации в следующем порядке:
DFSan использует списки ABI, чтобы определять, какие функции следует автоматически инструментировать, какие игнорировать, а для каких существуют пользовательские обёртки. См. документацию dfsan для получения дополнительной информации.
Попытки собрать большие программные проекты могут быть трудоёмкими, особенно старые или неподдерживаемые. Ещё более трудоёмко пытаться модифицировать систему сборки так, чтобы она поддерживала изменения, подобные инструментированию dfsan/нашему.
В polytracker/scripts есть скрипт, который можно запустить для любой ELF-библиотеки, и он выведет список функций для игнорирования. Мы используем его, когда не хотим отслеживать информацию, проходящую через конкретную библиотеку, например libpng, или другие подкомпоненты программы. Dockerfile-listgen.demo существует для сборки распространённых библиотек с открытым исходным кодом, чтобы мы могли создавать такие списки.
Этот скрипт — слегка модифицированная версия скрипта DataFlowSanitizer, ориентированная на игнорирование системных библиотек. Оригинальный скрипт можно найти в dfsan_rt.
Склонируйте этот Git-репозиторий. Из корня либо соберите базовый Docker-образ PolyTracker:
pip3 install -e ".[dev]" && polytracker docker rebuild
либо просто загрузите последнюю предварительно собранную версию с DockerHub:
docker pull trailofbits/polytracker:latest
Для демонстрации PolyTracker, работающего на парсере MuPDF, выполните эту команду:
docker build -t trailofbits/polytracker-demo-mupdf -f examples/pdf/Dockerfile-mupdf.demo .
mutool_track будет собран в /polytracker/the_klondike/mupdf/build/debug. Запуск mutool_track выведет polytracker.tdag, содержащий информацию, полученную в результате taint-анализа.
Для демонстрации PolyTracker, работающего на утилитах Poppler версии 0.84.0, выполните эту команду:
docker build -t trailofbits/polytracker-demo-poppler -f examples/pdf/Dockerfile-poppler.demo .
Все утилиты poppler будут находиться в /polytracker/the_klondike/poppler-0.84.0/build/utils.
cd /polytracker/the_klondike/poppler-0.84.0/build/utils
./pdfinfo_track some_pdf.pdf
Предположим, вы хотите немного глубже заняться расширением кодовой базы PolyTracker или анализом TDAG-трасс и не хотите возиться с локальным окружением, устанавливая сильно модифицированную версию LLVM.
Если вы работаете в Ubuntu и начинаете с относительно чистой базы 22.04 или 24.04, в связанном Gist описаны шаги для получения рабочей passthrough-версии базового контейнера PolyTracker. Базовый контейнер предоставляет среду разработки со всеми зависимостями, в которой можно работать напрямую или которую можно расширять (как мы сделали в примерах Dockerfile).
Модульные тесты Python и C++ следует запускать внутри Docker-контейнера PolyTracker.
Модульные тесты Catch2 в unittests/ находятся в /polytracker-build/unittests/src/taintdag/ внутри контейнера. Запустите тестовый двоичный файл внутри Docker-контейнера с помощью
cd /polytracker-build/unittests/src/taintdag/ && ./tests-taintdag
Модульные тесты Python в tests/ требуют локальных тестовых программ на C++, которые будут инструментировать тестовые фикстуры. Запустите их с помощью Pytest, находясь в рабочем каталоге
pytest tests
Или используйте pytest для запуска одного файла тестов с помощью
pytest tests/test_foo.py
В настоящее время PolyTracker работает только в Linux, так как это единственная система, поддерживаемая DataFlowSanitizer. Это ограничение связано лишь с отсутствием поддержки семантики системных вызовов для других ОС, которую можно добавить в будущем. Однако это означает, что для запуска PolyTracker в системе, отличной от Linux, потребуется установить Docker.
Taint-метки не будут распространяться через динамически загружаемые библиотеки, если только эти библиотеки не были собраны из исходных кодов с помощью PolyTracker, или в PolyTracker не реализована специальная поддержка вызовов этих библиотек. В настоящее время есть поддержка распространения taint через большинство вызовов неинструментированной стандартной библиотеки C. Стоит пояснить: программы, использующие неинструментированные функции, продолжат работать нормально, однако операции, выполняемые неподдерживаемыми библиотечными вызовами, не будут распространять taint. Сейчас мы работаем над добавлением надёжной поддержки программ на C++, но пока наилучшие результаты получаются на программах на C.
Если возникают проблемы с Docker, попробуйте выполнить system prune и сборку с --no-cache как для PolyTracker, так и для любой демонстрации, которую вы пытаетесь запустить.
Наихудший случай производительности PolyTracker возникает, когда один байт в памяти одновременно помечен большим количеством входных байтов из исходного файла. Это чаще всего происходит при инструментировании алгоритмов сжатия и шифрования с большими размерами блоков. В настоящее время исследуется и разрабатывается ряд способов смягчения такого поведения.
Вот некоторые из публично доступных результатов нашей работы с PolyTracker. Если вы знаете ещё что-то, что хотели бы видеть в этом списке, сообщите нам!
mapping и cavities), чтобы точно определить CVE, и написал об этом в блоге Trail of Bits.Это исследование было разработано Trail of Bits при финансировании Управления перспективных исследовательских проектов Министерства обороны США (DARPA) в рамках программы SafeDocs в качестве субподрядчика Galois. Оно распространяется по лицензии Apache 2.0. © 2019, Trail of Bits.
Пожалуйста, свяжитесь с нами по адресу [email protected].