В своей первой статье на Хабре я описывал опыт реверсинга и модификации проекта, доставшегося по наследству. Конечно, в отношении проекта на Python "реверсинг" - это гипербола, однако с чем-то ранее неизвестным столкнуться все же получилось. Если вкратце - вместо классических исходников использовались модули, загружаемые из .pyc, а не классических .py файлов. Философия "защитников" базируется на принципе "Там сложно, никто не разберется".  Ход событий же показал, что во-первых не так уж и сложно (передача параметров в хранимую процедуру PgSQL, и получение результата, возврат его пользователю - далеко не шедевр обфускации, скорее тут будет более применим принцип "Там несложно, любой разберется, но не захочет"), а во-вторых - кто-нибудь да поймет и найдет способ изменить поведение в нужном ключе.  Есть ли все-таки методы защиты исходников на python, и какие (относительно вменяемые) методы можно применять для решения этого вопроса? Заходят как-то в бар .pyo, .pyc, и .pyd… 	…а бармен им говорит "как дела в байт-коде, пацаны?" 	Перед тем, как разобраться в методах защиты исходных кодов, вспомним как устроено выполнение скриптов в Python. 	Реализация Python (в классическом случае это CPython), представляет собой компилятор и виртуальную машину. Скрипт, написанный на Python, преобразуется компилятором в байт-код, коий, в свою очередь, выполняется виртуальной машиной. 	Байт-код же состоит из кодов операций (опкодов) виртуальной машины, и сопутствующих опкоду аргументов. 	.py-файлы содержат исходный код, и выполняются виртуальной машиной, проходя этап преобразования в байт-код. 	Однако, можно каждый раз не преобразовывать .py-файлы в байт-код, да и зачем, если они, например, не меняются? Для хранения и выполнения байт-кода служат .pyc-файлы (их можно, например, наблюдать в директориях __pycache__). 	.pyc-файлы содержат готовый байт-код, непосредственно выполняемый виртуальной машиной. 	.pyo-файлы также как и .pyc-файлы содержат готовый байт-код, и, по-сути ничем, кроме предварительной оптимизации кода (вырезание assert`ов и docstring`ов) не отличаются. 	Кроме хранения байт-кода для виртуальной машины возможен сценарий, при котором исходный код на Python преобразуется в исходный код на языке C, и затем собирается в .pyd-файлы (для Windows) или .so для Linux. А вот в наше время… 	Вы когда-нибудь задумывались, почему на Python не делают crackmes? Вроде бы можно взять скрипт, преобразовать его в байт-код и…с той же легкостью преобразовать обратно в исходный код, каким-нибудь uncompyle. Да и отладка Python-скрипта - дело не сказать что очень сложное. Однако crackmes на python, хоть их и кот наплакал, но все же существуют.  Делятся они приблизительно на следующие группы: Код на Python, обернутый вместе с интерпретатором другим компилируемым языком, возможно преобразованный в .pyd .pyc-файлы, при создании которых использовался модифицированный интерпретатор, и стандартным интерпретатором они не выполняются Единственные найденные мной примеры crackmes на чистом Python. Демонстрируют, что код можно запутать. “Распутывание” сводится к расстановке переносов, удалении неиспользуемых символов, которые валидны синтаксически, однако не несут никакой смысловой нагрузки, и, возможно, недолгой отладке с модификациями на лету. 	Crackmes, как мерило того, насколько можно усложнить задачу “распутывания” кода, и ответа на вопрос “что же хотел сказать автор”, в случае с Python показывает, что голым Python-кодом в общем-то ежа не напугаешь (лишь бы не рассмешить). Modus operandi 	Зная основную теорию, можно выдвинуть ряд гипотез о точках внедрения защитных механизмов: .py  Если говорить о преобразовании кода на Python в другой код на Python, который делает ту же работу, однако менее читабелен, либо совсем нечитабелен, то на ум приходит слово “обфускация”.  Однако обфускация, как процесс изменения исходного кода, может в том числе приводить к изменениям в работе алгоритмов, например в быстродействии, что не есть хорошо. Единственным методом защиты на данном этапе может быть рекомендация писать настолько некачественный код, чтобы его модификация вошла в число опасных БДСМ-практик. .pyc \ .pyo Байт-код можно декомпилировать обратно в читаемый исходный код, что собственно и является главной опасностью. Следовательно, направление действий в данном случае - затруднение анализа и декомпиляции байт-кода. Структура .pyc-файла достаточно проста: Первые 2 байта это magic_number, указывающий на версию интерпретатора. 2 фиксированных байта 0x0D 0x0A 4 байта дата и время последней модификации Далее следует байт-код 		Для решения задачи невозможности декомпиляции байт-кода так или иначе придется модифицировать используемую реализацию Python, по-сути создавая кастомизированную версию виртуальной машины, поддерживающую привидение байт-кода к выполнимому виду. 	Запутать байт-код можно, применяя следующие техники: Шифрование. При компиляции дополнительно преобразовать весь байт-код в невыполнимый без стадии расшифровки. Обфускация опкодов. Суть метода заключается в дополнительном преобразовании непосредственно опкодов таким образом, чтобы "сдвинуть" опкод относительно его оригинального местоположения в таблице, превратив его в другой опкод, что, в свою очередь, сделает невозможным выполнение такого байт-кода немодифицированной версией виртуальной машины. 	Недостатками подобных методов являются: Необходимость поддержки при переходе на новые версии Python В случае с шифрованием - хранение ключа для расшифровки на машине с запускаемым кодом, либо имплементации протокола обмена ключами. В случае с обфускацией опкодов - возможность анализа сдвигов (при применении простых алгоритмов вроде шифра Цезаря), либо необходимость применения более сложных алгоритмов, с целью усложнения статического анализа. .pyd \ .so Самый эффективный, с точки зрения возможностей защиты метод - компиляция в исполняемый код (не байт-код виртуальной машины Python, а машинный код), и дальнейшее “запутывание”. Сама сборка .py-файла в библиотеку производится с помощью cython, и достаточно нетривиальна. В результате мы имеем код, который не имеет вообще ничего общего с изначальным исходным кодом, и декомпилирован быть не может (только дизассемблирован и исследован, в результате чего можно делать выводы об алгоритмах и особенностях кода).  В заключение хочу порекомендовать открытый урок «Знакомство с веб разработкой на Flask» от моих коллег из Otus, который пройдет 21 февраля. На этом вебинаре участники познакомятся с основами веб-разработки на Flask, а также научатся создавать и рендерить шаблоны страниц. Записаться можно на странице специализации Python-разработчик. Web-developer