Организация контролируемой сборки в статическом анализаторе Svace::Журнал СА 7-8.2017
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

  Опросы
1001 и 1 книга  
12.02.2021г.
Просмотров: 8522
Комментарии: 2
Коротко о корпусе. Как выбрать системный блок под конкретные задачи

 Читать далее...

11.02.2021г.
Просмотров: 8854
Комментарии: 3
Василий Севостьянов: «Как безболезненно перейти с одного продукта на другой»

 Читать далее...

20.12.2019г.
Просмотров: 16036
Комментарии: 0
Dr.Web: всё под контролем

 Читать далее...

04.12.2019г.
Просмотров: 15121
Комментарии: 13
Особенности сертификаций по этичному хакингу

 Читать далее...

28.05.2019г.
Просмотров: 16251
Комментарии: 6
Анализ вредоносных программ

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

Вебинар «Укрощение SIEM-системы. Инструкция по применению для системных администраторов»

 Организация контролируемой сборки в статическом анализаторе Svace

Архив номеров / 2017 / Выпуск №7-8 (176-177) / Организация контролируемой сборки в статическом анализаторе Svace

Рубрика: Наука и технологии

Без фото БЕЛЕВАНЦЕВ А.А., к.ф-м.н., ведущий научный сотрудник ИСП РАН, г. Москва, abel@ispras.ru

Без фото ИЗБЫШЕВ А.О., младший научный сотрудник ИСП РАН, г. Москва, izbyshev@ispras.ru

Без фото ЖУРИХИН Д.М., научный сотрудник ИСП РАН, г. Москва, zhur@ispras.ru

Организация контролируемой сборки
в статическом анализаторе Svace

В статье описываются методы организации контролируемой сборки приложений, необходимые для автоматического построения представления программ для последующего статического анализа их исходного кода. Обсуждаются технические особенности перехвата запуска процессов и вызова интерфейсов компиляции для ОС Windows и Linux. Приводятся экспериментальные результаты оценки временных затрат на контролируемую сборку

1. Введение

Статический анализ исходного кода программ для поиска критических ошибок является общепринятым методом повышения качества программ и применяется в жизненном цикле разработки безопасного ПО. Для успешного промышленного внедрения анализатора недостаточно реализовать в нем теоретические методы, обеспечивающие необходимое качество анализа. Нужно также выполнить ряд нефункциональных требований к анализатору, в числе которых – организация автоматического анализа кода, по возможности без участия пользователя. Это означает, что разбирать исходный код программы и строить внутреннее представление для анализа нужно автоматически.

Однако для успешного разбора исходного файла потребуется знать, как именно предполагалось скомпилировать данный файл: так, для языков Си/Си++ необходимо правильно задать пути к каталогам с заголовочными файлами, предопределенные макросы компилятора, целевую архитектуру и т.п., для языка Java – пути к JAR-библиотекам и исходным кодам-зависимостям, версию языка. Чтобы получить эту информацию, возникает идея провести контролируемую сборку программы – предполагая, что все нужные данные передаются утилитам сборки, запустить процесс сборки под контролем анализатора, отследить запуски всех интересующих утилит, получить требуемую информацию из ихпараметров и окружения, организовать запуск собственных компонент анализатора.

Цель статьи – описание устройства контролируемой сборки в анализаторе Svace [1-2], разрабатываемом в Институте системного программирования РАН. Далее в разделе 2 приводится общая схема выполнения сборки, в разделе 3рассказывается о перехвате событий сборки, в разделе 4 – о том, как анализатор реагирует на эти события. В разделе 5 приводятся краткие сведения о реализации компонента мониторинга и результаты замеров его производительности. Раздел 6 заключает статью.

2. Устройство контролируемой сборки

Схематически устройство контролируемой сборки представлено на рис. 1. Изначальная сборка преобразует исходный код программы в артефакты сборки – исполняемые файлы, библиотеки, документацию и т.п. При контролируемой сборке выполняется мониторинг исходного процесса сборки без влияния на него и отслеживаются так называемые события сборки – выполнение одного из интересующих нас действий. На каждое такое событие готовится и выполняется реакция по сбору нужной для анализа информации. Примеры часто встречающихся событий и реакций показаны на рисунке.

Рисунок 1. Контролируемая сборка программы

Рисунок 1. Контролируемая сборка программы

Подробнее об организации каждого из этапов отслеживания событий и выполнения реакций будет рассказано далее. До этого заметим, что при наступлении события сборка или ее часть приостанавливается до окончания отработки реакции на событие. Причина выбора синхронной обработки в том, что нужная анализу информация может быть недоступна после окончания сборки или даже после выполнения интересующей нас утилиты сборки. Например, используемые библиотеки могут быть перемещены или удалены, сгенерированные исходные коды также удалены и т.п. Асинхронная обработка приведет к тому, что часть информации может быть потеряна (в случае параллельной сборки – недетерминировано утеряны разные данные, что может привести к различным результатам анализа в зависимости от сборки и крайне нежелательно).

3. Обнаружение событий сборки

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

Классификация методов решения упомянутых задач представлена на рис. 2.

Рисунок 2. Определение событий сборки

Рисунок 2. Определение событий сборки

Наиболее часто встречаемым случаем события является запуск внешнего процесса (компиляции, компоновки и т.д.). Способы перехвата процессов и получения их параметров и окружения разнятся между операционными системами, номожно выделить несколько основных (рассмотрим на примере ОС семейств Windows и Linux).

1) Подмена функций системных динамических библиотек, создающих новые процессы, через инъекцию DLL – подгрузку собственной динамической библиотеки, реализующей нужные функции.

Для Linux такая возможность предоставляется через механизм LD_PRELOAD, управляющий поведением динамического загрузчика, для Windows – например, через функцию CreateRemoteThread из Windows API (собственно инъекция) иподмену адресов функций в таблице импортов загруженного образа kernel32.dll. Этот способ является основным способом перехвата для Linux. Причина заключается в низких накладных расходах и простоте доступа к пути к исполняемому файлу, параметрам и окружению процесса (т.к. эти данные напрямую передаются в функции семейств exec и posix_spawn).

Особое внимание следует уделить сохранению контракта подмененных функций. В частности, в Linux при подмене функции execve нужно учитывать, что она может быть вызвана в дочернем процессе, порожденном с помощью функции vfork, в результате чего возникают технические сложности как вследствие исполнения в адресном пространстве родительского процесса, так и, в случае синхронной реакции на событие, из-за блокирования родительского потока исполнения до вызова execve либо завершения процесса.

Основное ограничение заключается в неприменимости данного способа к статически скомпонованным программам в Linux (в Windows из-за невозможности статической компоновки с системными библиотеками, реализующими Windows API, проблема отсутствует).

Кроме того, возможны ограничения на передачу относящихся к загрузчику переменных окружения при запуске set-user-id/set-group-id процессов (например, переменная LD_PRELOAD будет проигнорирована). Для их обхода, например, утилита sudo позволяет указать переменные окружения, которые необходимо сохранить при запуске дочерних процессов. Для этого требуется сконфигурировать утилиту специальным образом.

2) Мониторинг создания процессов штатными средствами отладки операционной системы – для ОС Linux через функцию ptrace, для ОС Windows – через Windows Debugging API.

Этот способ – основной при работе для систем Windows, а для Linux используется при обнаружении статически скомпонованных процессов. Возникающие сложности технического характера также зависят от системы, но можно выделить общую проблему – получение данных о процессе из другого процесса. Так, в Linux запрещена отладка привилегированных процессов непривилегированными, поэтому могут быть случаи, когда необходимо запускать контролируемую сборку с правами привилегированного пользователя, а это не всегда возможно. Другой проблемой является получение пути к запущенному исполняемому файлу в том виде, в котором он был передан в системный вызов execve – нужно делать трассировку системных вызовов, т.к. путь в исходной форме не попадает ни в какие системные структуры данных, связанные с созданным процессом. Это необходимо на некоторых файловых системах, где канонические пути – этоуникальные идентификаторы специального вида, по которым напрямую нельзя определить, является ли процесс интересным для анализатора.

На Windows мониторинг через интерфейсы отладки, вообще говоря, меняет поведение отлаживаемого процесса, например, память через Windows API выделяется другим аллокатором, функция CloseHandle может выбросить исключение приобработке некорректных дескрипторов объектов и др. В результате может измениться поведение исходной сборки программы, что и наблюдалось в очень редких случаях.

3) Мониторинг создания процессов через интерфейсы ядра ОС (модули ядра, драйверы), т.е. создание специальных компонент, выполняемых с привилегиями ядра ОС.

Такой способ, например, используют антивирусные программы на ОС Windows. В анализаторе Svace этот метод не применяется из-за желания минимизировать влияние на машину, на которой производится сборка. Установка новых компонент на уровне ОС может быть запрещена на промышленно используемых в компании серверах сборки – ошибка в коде мониторинга с большой вероятностью приведет к краху системы.

4) Замена исполняемых файлов интересующих анализатор процессов на собственные обертки, которые при запуске сообщают анализатору о произошедшем событии запуска, а также выполняют и оригинальный файл.

Этот способ исторически был первым, поддерживаемым в Svace, из-за легкости его реализации, после чего от него отказались в пользу остальных перечисленных способов – создание обертки требует вмешательства в окружение сборки илив сам процесс сборки. Сейчас обертка используется в исключительных случаях тогда, когда остальные способы не работают, например для перехвата 16-битных компиляторов в виртуальной DOS-машине NTVDM.

Следующим возможным типом события является запуск компиляции без создания нового процесса. Это происходит, когда компилятор может использоваться как библиотека через некоторый интерфейс компиляции. Например, в случае языка Java основной компилятор Javac пакета OpenJDK может вызываться через Java Compiler API. Другие компиляторы Java (ECJ, компилятор Jack из ОС Android версий 6-8) также реализованы на Java и поддерживают аналогичную возможность. Наконец, популярные среды сборки Java Ant, Maven, Gradle обычно выполняют компиляцию приложений через такие интерфейсы.

В описанных случаях событие сборки является вызовом некоторого метода в виртуальной машине Java. Для перехвата вызовов в Java начиная с версии 1.5 можно использовать инструментирование байт-кода виртуальной машины, которое выполняется через стандартный механизм Java-агентов [3]. Агентом обычно является библиотека на языке Java, которой виртуальная машина сообщает о загрузке любого класса и предоставляет возможность изменить этот класс произвольным образом, передавая библиотеке двоичное представление класса (массив байт).

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

Для внедрения собственного Java-агента в виртуальную машину Java необходимо отслеживать запуск виртуальных машин наряду с остальными процессами, т.е. такой запуск становится событием сборки, на которое требуется реакция анализатора – добавление к командной строке запуска виртуальной машины опции с указанием дополнительного Java-агента.

Наконец, третий вариант события сборки – запрос на компиляцию, посланный серверу компиляции, постоянно работающему в ходе всей сборки (например, для языка C# в Microsoft Visual Studio и утилите сборки MSBuild используется сервер VBCSCompiler.exe; при сборке ОС Android компилятором Jack также используется сервер, принимающий команды компиляции через HTTP-запросы). Для перехвата таких событий нет каких-либо универсальных рецептов. Если сервер функционирует в виртуальной машине, можно попытаться свести задачу к предыдущей, инструментировав код сервера через штатные средства машины. В случае MSBuild можно подключить собственную библиотеку (логгер), которая будет оповещаться утилитой о событиях сборки. Мы реализовали такую библиотеку для перехвата компиляций для языка C#. Можно заметить, что для внедрения библиотеки также нужно перехватывать запуски самой утилиты MSBuild. А самым простым, но не всегда приемлемым, решением является отказ от использования сервера компиляции.

В дополнение к изложенным вариантам возникновения и перехвата событий сборки перечислим следующие проблемы организации перехвата общего характера:

  • Завершение контролируемой сборки. В некоторых случаях системы сборки могут порождать служебные процессы или серверы компиляции, которые не завершаются немедленно после окончания сборки. Следовательно, ждать завершения всех дочерних процессов, созданных при сборке, нельзя. Разумным компромиссом является окончание контролируемой сборки сразу после завершения основной команды сборки.
  • Перехват событий, возникающих при выполнении процессов для другой аппаратной архитектуры. В некоторых системах сборки компиляция программ для встраиваемых систем происходит не с помощью кросс-компилятора для этой системы, а с помощью вызова эмулятора, который выполняет обычный компилятор для встраиваемой системы, предназначенный для работы непосредственно на этой системе. Например, для компиляции программ для архитектуры ARM на машине семейства X86 запускается эмулятор QEMU, который выполняет компилятор ARM в двоичных кодах архитектуры ARM. В этом случае для поддержки перехвата процессов через LD_PRELOAD необходимо собрать библиотеку перехвата для архитектуры ARM. Также нужно отметить, что внутри эмулятора QEMU в пользовательском режиме (usermode) функция отладки ptrace вообще не реализована.
  • Перехват событий сборки в серверах компиляции, уже запущенных в момент начала контролируемой сборки. Для внедрения в серверы компиляции собственного мониторинга желательно контролировать момент их запуска. Если же сервер уже работает, то перехват событий сборки в нем затруднен, хотя технически может быть возможен, если реализовать функциональность, схожую с подключением отладчика к уже работающему процессу. Наиболее простое решение – вмешательство пользователя для подготовки окружения контролируемой сборки.
  • Поддержка систем распределенной сборки. Все описанные методы работают на одной вычислительной машине. Если большая программная система собирается распределенно, то, как правило, организуется контролируемая сборка накаждом узле системы, а потом полученные данные объединяются в один набор, который далее можно анализировать совместно. Например, при сборке операционной системы Tizen через OBS [4] на каждый узел системы инсталлируется компонент мониторинга Svace в виде отдельного RPM-пакета, а после окончания сборки полученные результаты со всех узлов объединяются специальной утилитой так, чтобы впоследствии с точки зрения анализатора казалось, что сборка происходила на одном компьютере.

4. Выполнение реакции на события

Возможные реакции на события сборки не сводятся только к построению внутреннего представления. Как упоминалось выше, нужно собрать данные для обеспечения работы всех этапов анализа. Можно выделить следующие виды реакции (см. также рис. 2):

1) Запуск собственного компилятора для построения представления для анализа. Это основной тип реакции на события сборки. Для обработки события нужно извлечь настройки компиляции из параметров и окружения перехваченного пользовательского компилятора и адаптировать их для настройки запуска собственного компилятора. Сложность заключается в том, что вариантов компиляторов для Си/Си++ – десятки, для Java – три основных. Существенными дляуспешной компиляции настройками являются: пути к используемым заголовочным файлам, библиотекам и т.п.; набор заранее определенных пользовательским компилятором макросов (для Си/Си++); диалект языка; целевая архитектура иее свойства. Выяснить некоторые из этих настроек по команде компиляции не всегда возможно, и приходится организовывать запуск исходного компилятора со специальными опциями и разбирать его вывод. Для разбора опций компиляции также часто требуется читать файлы с опциями, имена которых находятся в командной строке.

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

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

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

3) Сбор исходных кодов и библиотек. Некоторые используемые сборкой файлы требуется сохранить для дальнейшего использования анализатором. Примерами таких файлов являются файлы с исходным кодом для показа предупреждений анализа, библиотеки Java в формате JAR, анализ которых проводится перед анализом пользовательского кода, файлы с данными, требуемые для некоторых специфичных детекторов (например, манифесты Android-приложений).

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

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

5) Ведение журнала сборки. Все поступающие события сборки записываются в специальный журнал, который бывает полезен при отладке компонентов мониторинга и при добавлении поддержки новых компиляторов и инструментов.

5. Реализация и экспериментальные результаты

Описанные методы организации контролируемой сборки реализованы в анализаторе Svace следующим образом. Для операционной системы Linux поддерживаются методы перехвата событий сборки через LD_PRELOAD и отладку процессов, для Windows – только через отладку. Компоненты, ответственные за перехват и выделение интересующих событий, реализованы на языке Си, а трудоемкая часть обработки данных, необходимая для запуска собственных компиляторов, – на языке Питон.

Ядро библиотеки перехвата позволяет регистрировать расширения для обработки события. В общем случае расширение должно уведомить ядро о том, что данное событие ему интересно, после чего ядро передает соответствующему обработчику всю информацию о перехваченном процессе. Под этот интерфейс подпадает большинство не связанных с компиляторами расширений. Для обработки компиляторов, как важного и самого трудоемкого случая, реализован более детальный интерфейс расширений, включающий построение команд с исходным компилятором для запроса путей к заголовочным файлам и встроенных макросов, раскрытия опций компилятора (если требуется чтение файлов с опциями), для компиляторов Си/Си++ – определение конкретного языка компиляции. Расширения делают удобным добавление поддержки нового компилятора – для этого достаточно добавить новое расширение и реализовать соответствующие обработчики интерфейса к ядру перехвата.

Для языков Си/Си++ поддерживается более 20 компиляторов, включая популярные компиляторы GCC, Clang, MSVC, ARMCC, а также ряд компиляторов для встраиваемых систем. Для языка Java поддерживаются три компилятора – основной компилятор Java из пакета OpenJDK, а также компиляторы ECJ и Jack, как для запусков из командной строки, так и через интерфейсы компиляции. Для языка C# поддерживается основной компилятор Microsoft и компилятор Mono через утилиты MSBuild и xbuild.

Кроме того, поддерживается возможность пользовательской конфигурации распознавания компилятора и задания опций компиляции через создание специального файла в формате JSON. Это бывает полезно в случаях, когда стандартное определение типа запущенного компилятора не срабатывает, например, для специфических компиляторов на основе GCC, при конструировании которых была изменена структура каталогов служебных компонент компилятора. Пользователь может указать, что исполняемый файл по заданному пути является компилятором известного типа, либо доопределить или убрать некоторую опцию компиляции, либо выключить перехват сборки определенных файлов.

В таблице 1 приводятся результаты замеров производительности контролируемой сборки на системах Windows и Linux. В ходе замеров были выключены все расширения, выполняющие обработку реакции на события, кроме ведения журналов сборки, так, чтобы можно было оценить затраты собственно на организацию перехвата. Были выбраны приложения среднего размера – пакет языка Php и SMT-решатель Z3 [5], которые собирались дважды – с последовательной ис параллельной сборкой (в 8 потоков на Windows и в 16 на Linux). В каждом случае проводилось по пять запусков исходной сборки и сборки с включенным мониторингом, из которых выбрасывались два крайних по времени результата, аостальные усреднялись.

Таблица 1. Производительность перехвата сборки

Приложение ОС/Вид перехвата Время сборки (с), последовательно параллельно Время сборки (с),
Без перехвата С перехватом Без перехвата С перехватом
Php Windows / debug 95 96 (+1%) 57 60 (+5.2%)
Z3 Windows / debug 473 488 (+3.1%) 209 224 (+6.2%)
Php Linux / LD_PRELOAD 570 589 (+3.3%) 115 118 (+2.6%)
Z3 Linux / LD_PRELOAD 1358 1369 (+0.8%) 175 174 (-0.5%)
Php Linux / ptrace 570 604 (+5.9%) 115 122 (+6.0%)
Z3 Linux / ptrace 1358 1379 (+1.5%) 175 175 (0.0%)

6. Заключение

В статье описана организация контролируемой сборки программ, необходимая для выполнения статического анализа их исходного кода. Приведенные методы реализованы в анализаторе Svace и показали применимость на практике – замедление сборки из-за мониторинга не превышает 6%. Планы улучшения системы включают перенос большинства расширений на язык Питон для упрощения обработки и эксперименты с поддержкой перехвата функций создания процессов на ОС Windows.

  1. В.П. Иванников, А.А. Белеванцев, А.Е. Бородин, В.Н. Игнатьев, Д.М. Журихин, А.И. Аветисян, М.И. Леонов. Статический анализатор Svace для поиска дефектов в исходном коде программ. Труды Института системного программирования РАН. Том 26, 2014 г., стр. 231-250.
  2. А.Е. Бородин, А.А. Белеванцев. Статический анализатор Svace как коллекция анализаторов разных уровней сложности. Том 27, вып. 6, 2015 г., стр. 111-134.
  3. Инструментация байт-кода Java через Java-агенты – https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html (дата обращения 20.06.2017).
  4. Система сборки OBS – http://openbuildservice.org/ (дата обращения 20.06.2017).
  5. SMT-решатель Z3 – https://github.com/Z3Prover/z3 (дата обращения 20.06.2017).

Ключевые слова: перехват запуска процессов, динамическая загрузка, отладка, Java-агенты.


Monitoring program builds for Svace static analyzer

Andrey Belevantsev, Leading researcher, ISP RAS, Moscow, abel@ispras.ru

Alexey Izbyshev, Junior researcher, ISP RAS, Moscow, izbyshev@ispras.ru

Dmitry Zhurikhin, Researcher, ISP RAS, Moscow, zhur@ispras.ru

Abstract: The paper describes methods for performing controlled program builds with the goal of constructing the program’s representation for further static analysis. We discuss the specifics of intercepting process launches and compiler API calls for Windows and Linux. We show experimental evaluation of the controlled builds overhead.

Keywords: process interception, dynamic loading, debugging, Java agents.


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru