Мы уже писали о распространенных способах защиты исходного кода Python от реверс‑инжиниринга и принципах их обхода. В сегодняшней статье я продолжу эту тему и расскажу об особенностях реверса кастомной нестандартной реализации Python-интерпретатора.
warning
Статья написана в исследовательских целях, имеет ознакомительный характер и предназначена для специалистов по безопасности. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Использование или распространение ПО без лицензии производителя может преследоваться по закону.
В качестве примера возьмем некое приложение для нейросетевой обработки видео с нехорошей особенностью: приложение проверяет лицензию на удаленном сервере при выполнении всех более‑менее полезных функций. Если проверка завершается успешно, оно продолжает работать офлайн, что дает нам надежду на возможность запускать программу без подключения к интернету.
То, что это классическое Python-приложение, видно с первого взгляда, даже без запуска Detect It Easy.
Поскольку рядом с исполняемым модулем лежат питоновские библиотеки, а подкаталоги заполнены файлами с расширением .
, никаких сомнений в использовании Python не остается. Что это за файлы и с чем их едят, я подробно рассказывал в статье «Змеиная анатомия. Вскрываем и потрошим PyInstaller». В двух словах поясню для тех, кто не читал эту публикацию: упомянутые бинарные файлы содержат прекомпилированный байт‑код, подаваемый непосредственно на вход интерпретатора для ускорения обработки. В этой же статье приведены и известные декомпиляторы и дизассемблеры файлов подобного формата (uncompyle6, pycdc, pydasm...).
На этой оптимистичной ноте можно было бы и закончить статью, но нет. Несмотря на валидный заголовок .pyc-файла с сигнатурой версии 3.10 (выделено красным), ни один из стандартных декомпиляторов или дизассемблеров не ест такие файлы, выдавая ошибку типа CreateObject:
. При просмотре файла в HEX-редакторе мы видим, что он весь, собственно, и состоит из явно зашифрованного объекта нестандартного типа (выделено желтым, 0xDD
).
Налицо явно кастомная нестандартная питоновская реализация, благо, как я уже говорил, исходный код Python в сети присутствует, и нет ничего сложного подогнать его под свои нужды. По счастью, шифрование здесь используется явно не шибко взрослое. На скриншоте прослеживается повторяющийся через каждые 16 байт шаблон, который, похоже, наложен операцией XOR на исходный код и местами проглядывает на месте нулевых последовательностей.
Поискав в нативных библиотеках явный фрагмент такого шаблона (например, 25
), мы тут же натыкаемся в файле python311.
на шаблон (выделено красным) и код, расшифровывающий данные сразу после чтения из файла.
Чуть поигравшись с отладчиком x64dbg, мы обнаруживаем и место вызова этого кода: Py_Main
, начиная с которого в отладчике можно следить за расшифровкой и интерпретацией байт‑кода.
Попробуем для начала добиться какой‑то более‑менее понятной декомпиляции .pyc-файлов. Для этого напишем простенький расшифровщик, расксоривающий объект 0x5D
полученной маской. Благодаря этому исходный файл удается привести в гораздо более читаемый вид.
К сожалению, этот код, хоть и сильно похож на настоящий, все равно не работает. На этот раз вылезает ошибка CreateObject:
. У меня возникло сильное подозрение на нестандартный маршалинг объектов внутри .pyc-файла.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее