Комментарии 6
Я понимаю, что мой опыт вряд ли обобщаем до автоматизации, но все же поделюсь своим подходом.
Если мне нужно проанализировать большой проект под линукс, то я начинал с создания автономного стенда для проекта, который бы позволял собирать исходники (благо, чаще всего такой стенд могли предоставить разработчики продукта). Дальше просто по стандартным этапам:
1) Разведка. Ищем все возможные компиляторы в системе. Нередко разработчики пользуются chroot'ом, так что слоям данных внутри chroot'а отдельные поиски.
2) Сбор первоначальной информации. Пишется простейший прокси-компилятор, которые собирает информацию о том как его вызвали (грубо говоря, argc, argv, envp) в файл и вызывает бинарник с приписанным к имени -orig. Дальше все настоящие компиляторы перемещались с этим самым дописанным -orig, а на их место помещался свеженаписанный прокси. Убеждаемся, что сборка проходит, получаем первую статистику о том, какие файлы и с какими параметрами компилируются.
3) Внедрение анализаторов. Дальше функциональность прокси-компилятора увеличивается - кроме вызова оригинального компилятора ему навешиваются задачи по вызову анализаторов. Решаются мелки типовые задачи: вызываем оригинальный gcc с расширенным набором проверок, но нужно убрать Werror, если есть, поменять имя выходного файла, чтобы он не мешал. Для вызова cppcheck нужно некоторые параметры переделать или убрать. Для scan-build'а тоже нужно правильно параметры передать. Для других анализаторов тоже свои мелкие доработки приходится делать.
4) Собираем результаты и уже вручную их разбираем.
Для Windows тоже делал подобное - подменял cl.exe на свой прокси, но там что-то не срасталось. В итоге пошел по пути dll hijack - подсовываю свою version.dll к cl.exe и в нем фильтрую чтобы убедится, что вызывают компилятор. Уже из библиотеки вызываются анализаторы.
Раз что-то уже от рута работает, то оно может подключаться как отладчик к процессу, и по CreateProcess подключаться к дочерним процессам. Лучше чем хуки/инжекты т.к. официальный API и типа не троян.
Отсюда идут проблемы прошлой реализации. Можно не успеть подключиться к моменту старта сборки, пропустить часть порождённых процессов и упустить соответствующие исходные файлы.
В текущей схеме админские права не влияют на процесс отслеживания. Просто позволяют прописаться в реестр, чтобы поставить «барьер». Тогда стартующие процессы сами придут к нам в руки. Собственно последующая сборка выполняется от того пользователя, который её запустил. Без оглядки на сервер мониторинга.
Как сделать свой отладчик для трассировки порожденных процессов я пока не разбирался. Это конечно может помочь в будущем сделать трассировщик. Но в нём хочется обойтись без повышенных прав, и грубых манипуляций со сборочной системой (типа подмены бинарников компилятора), и лишних действий со стороны пользователя.
А какой процесс прикажете хукать? Ну можно, конечно, встроиться в devenv.exe
Но что-то мне подсказывает, что нужно будет охватить более обширную аудиторию,
а это как минимум cmake.exe плюс все его потомки, в том числе ninja.exe и vcpkg.exe
Но если отслеживать потомков, тогда драйвер проще, он встроится непосредственно
в ядерный kernel32.exe и его CreateProcess
Я кстати, делал как-то свой монитор, но не такой хитрый, а по-колхозному, через подмену файлов cl.exe link.exe lib.exe
set VCP=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\bin
copy "%VCP%\Hostx86\x86\cl.exe" "%VCP%\Hostx86\x64\old_cl.exe"
copy "%VCP%\Hostx86\x86\lib.exe" "%VCP%\Hostx86\x64\old_lib.exe"
copy "%VCP%\Hostx86\x86\link.exe" "%VCP%\Hostx86\x64\old_link.exe"
copy monitor.exe "%VCP%\Hostx86\x64\cl.exe"
copy monitor.exe "%VCP%\Hostx86\x64\lib.exe"
copy monitor.exe "%VCP%\Hostx86\x64\link.exe"
И дальше из монитора определялось кто я по argv[0] и вызывался старый экзешник с префиксом old_ со всеми параметрами плюс этот вызов логгировался в файл, причем один только вызов оказалось логгировать недостаточно, нужно еще сохранять все переменные окружения char *envp[] и только тогда получим "компилятор в собственном соку".
При этом случился кринж, когда я читал msdn, открытый на описании функции main :
int _tmain (int argc, _TCHAR * argv [], _TCHAR * envp [])
и тут подошёл коллега со словами - ну всё, дожили, ты уже main читаешь!
Мне кажется, что не проблема скопировать код инъекции в перехваченный CreateProcess и сделать процесс рекурсивным. У любой системы сборки есть корневой процесс будь то cmake.exe или ещё какой. Можно вообще не разбираться в деталях системы сборки, а только взять у юзера команду сборки (или внедриться в процесс IDE вообще) и только знать про то как зовут компилятор, чтобы логгировать его вызовы, когда до них дойдёт.
Новый механизм мониторинга компиляции в PVS-Studio для Windows