Низкий порог входа, простота использования, высокая скорость разработки и огромная база библиотек сделали Python популярным языком программирования. Разработав программу и перейдя к стадии ее распространения многие задаются вопросом защиты ПО.
Обфускация один из методов защиты приложений. Защиту данных паролями и привязкой к железу легко обойти, проанализировав код программы. Чтобы код нельзя было так просто просмотреть и понять его необходимо запутать усложнить для восприятия и анализа. Для этого и была разработана обфускация, набор методов, превращающих понятный код в непонятный набор символов.
Причин для обфускации может быть несколько:
- защитить идеи от копирования;
- ограничить круг лиц способных поддерживать код;
- скрыть какие-то значения или действия например, если в программу встроена проверка пароля;
- защитить алгоритм от взлома.
Обфускация соответствует принципу экономической целесообразности, ее использование экономически выгодно для небольших проектов, стоимость программного продукта повышается незначительно, и позволяет при этом уменьшить риски несанкционированного доступа к программному коду, а также когда другие методы являются неэффективными или нецелесообразными, например браузерные приложения выполняющиеся на стороне клиента.
Как защитить свой код на Python от ВЗЛОМА
Виды обфускации
- лексическая обфускация;
- преобразование данных;
- преобразование управления;
- профилактическая обфускация;
- обфускация адресов;
- регулярное обновление кода.
Лексическая обфускация замена названий переменных и функций.
Преобразование данных более сложная и эффективная операция. Она включает в себя изменение и создание новых типов данных и применение к ним комбинаторики.
Преобразование управления нарушение естественного хода программы, для чего используются непрозрачные предикаты. То есть это случай, когда результат исполняемых действий трудно предугадать в ходе заданной процедуры. В простейшем случае это создание дополнительных блоков кода: в одном выполняются вычисления, в другом происходит наследование, а в каком-то общем несколько ложных операций, лишь одна из которых действительная.
Профилактическая обфускация защищает ваш код от деобфускации специальными программами-деобфускаторами.
Обфускация адресов д анный метод изменяет структуру хранения данных , так чтобы усложнить их использование. Например алгоритм, может выбирать случайными адреса данных в памяти, а также относительные расстояния между различными элементами данных. Данный подход примечателен тем, что даже если злоумышленник и сможет «декодировать» данные, используемые приложением на каком-то конкретном устройстве, то на других устройствах он всё равно не сможет воспроизвести свой успех.
Регулярное обновление кода метод предотвращает атаки, регулярно выпуская обновления обфусцированного программного обеспечения. Делают усилия по взлому кода в таком случае экономически нецелесообразными.
Защитите свой код на Python от взлома — PYARMOR Обфускация
При обфускации кода, важно правильно оценить, какие части когда можно эффективно запутать. Следует избегать обфускации кода, критичного относительно производительности.
У обфускации есть два больших недостатка:
- Программа работает медленнее.
- Обфусцированный код почти невозможно править без доступа к исходному коду.
Python предоставляет библиотеки для обфускации и защиты программного кода. Наиболее популярной и востребованной библиотекой является PyArmor.
PyArmor это инструмент, используемый для обфускации скриптов Python, привязки скриптов на к конкретному ПК, а также установлении срока действия скриптов.
Устанавливаем библиотеку через командную строку:
pip install pyarmor
В предыдущей статье, мы создали игру змейка .
Командой obfuscate проведем обфускацию кода игры.
Запустим командную строку. Перейдем в папку с проектом и выполним команду:
pyarmor obfuscate snake_game.py
В результате получаем папку dist с файлами snake_game.py и файл dll.
Модуль pyarmor позволяет устанавливать ограничения по сроку действия программы файлы лицензии. Для этого выполните следующую команду:
pyarmor licenses —expired 2022-12-31 r001
pyarmor obfuscate —with-license licenses/r001/license.lic snake_game.py
Библиотека имеет множество других полезных функций, ознакомится с которыми можно в документации .
В данной статье, мы рассмотрели методы защиты программного кода, изучили библиотеку pyarmor, а также рассмотрели методы библиотеки.
Источник: dzen.ru
Об одном способе защиты исходников Python-программы
Однажды мне пришлось участвовать в разработке одного небольшого проекта для научных расчётов, который разрабатывался на языке программирования Python. Изначально Python был выбран как удобный и гибкий язык для экспериментов, визуализации, быстрого прототипирования и разработки алгоритмов, но в дальнейшем стал основным языком разработки проекта.
Надо заметить, что проект был хоть и не большим, но довольно насыщенным технически. Для обеспечения требуемой функциональности, в проекте широко применялись алгоритмы теории графов, математическая оптимизация, линейная алгебра и статистика. Также использовались декораторы, метаклассы и инструменты интроспекции. В процессе разработки пришлось использовать сторонние математические пакеты и библиотеки, например, такие как numpy и scipy, а также многие другие.
Со временем стало ясно, что переписывать проект на компилируемом языке слишком затратно по времени и ресурсам. Скорость работы и потребление памяти не являлись критичными показателями в данном случае и были вполне приемлемыми и достаточными. Поэтому было принято решение оставить всё как есть, и продолжить разработку и поддержку проекта на языке Python. К тому же, документация по большей части уже была написана с использованием Sphinx.
Проект являлся библиотекой, функции которой использовались в одном из модулей расширения в крупном программном комплексе. Программный комплекс был написан на C++, являлся коммерческим продуктом, имел защиту с аппаратным ключом и поставлялся клиентам без предоставления исходных кодов.
Здесь сразу обозначилась новая проблема: как защитить исходные коды нашей Python-библиотеки? Может быть, в ином случае никто бы не стал этим заниматься, я бы уж точно, но в библиотеке были реализованы некоторые ноу-хау, и руководители проекта не хотели, чтобы данные наработки попали к конкурентам. Так как я был одним из исполнителей, мне пришлось озаботиться данной проблемой. Далее я постараюсь рассказать об основной идее, что из этого вышло, и как нам удалось скрыть Python-исходники от лишних глаз.
Что предлагают люди
Как известно, наверное, большинству разработчиков, Python — язык интерпретируемый, динамический с богатыми возможностями интроспекции. Бинарные файлы модулей *.pyc и *.pyo (байт-код) легко декомпилируются, поэтому распространять их в чистом виде нельзя (если уж мы решили не показывать исходники по-настоящему).
Как, я думаю, любой на моём месте, сначала я решил поискать, а что вообще делают люди в таких случаях? Первые же поисковые запросы показали, что люди не знают, что делать и спрашивают об этом на stackoverflow и в других местах, например, вот вопрос на stackoverflow. Поискав, я пришёл к выводу, что везде предлагают несколько спорных способов:
- Забить и не париться, всё равно, кому надо — расковыряет;
- Переписать на компилируемом языке;
- Сделать обфускацию исходников, например с помощью раз и два;
- Транслировать все Python-модули в модули расширения (*.pyd) с помощью Cython или Nuitka (как сделал warsoul — автор данной статьи);
- Заменить опкоды в исходниках Python-интерпретатора и распространять свою сборку, как предлагалhodik.
По многим причинам я отбросил все эти способы как неподходящие. Например, обфускация Python-кода. Ну какая может быть обфускация, когда синтаксис языка построен на отступах, а сам язык пронизан «хитрой интроспекцией»? Транслировать все Python-модули в бинарные модули расширения тоже не представлялось возможным, т. к. проект, напомню, был достаточно сложным технически с использованием множества сторонних пакетов, да и сам состоял из большого числа модулей в многоуровневой иерархии пакетов, которые было утомительно перегонять в *.pyd, а потом ловить баги, вылезающие на ровном месте. Возиться с заменой опкодов не хотелось, так как пришлось бы распространять и поддерживать собственную сборку интерпретатора Python, да ещё и компилировать им Python-модули всех используемых сторонних библиотек.
В какой-то момент мне показалось, что эта идея с защитой Python-исходников бесполезная, надо всё это бросить и убедить руководство, что ничего не выйдет и заняться чем-нибудь полезным. Отдаём *.pyc файлы и ладно, кто там будет разбираться? Убедить не получилось, переписывать библиотеку на C++ никому не хотелось, а проект нужно было сдавать. В итоге всё же кое-что получилось сделать. Об этом, если всё ещё интересно, можно прочитать далее.
Что сделали мы
Что может лучше всего защитить какую-либо информацию на цифровом носителе от посторонних? Я думаю, что это шифрование. Вооружившись этой фундаментальной идеей, я решил, что исходники надо шифровать, а иначе и быть не должно. Для стороннего наблюдателя, который начал проявлять излишний интерес, всё это должно выглядеть как куча непонятных файлов с непонятным содержимым. Вполне себе обфускация, но более продвинутая чем заменять имена переменных и вставлять пустые строчки.
Ход моих мыслей был следующим:
- Шифруем каким-либо способом все исходники нашей Python-библиотеки, можно их даже перемешать и изменить имена файлов модулей и пакетов;
- Пишем обвязку для того, чтобы Python-интерпретатор умел загружать и импортировать модули из зашифрованных текстовых файлов (расшифровка, восстановление структуры пакетов и имён файлов, импорт и т. д.);
- «Прячем» всё это в бинарный модуль расширения (*.pyd), чтобы никто не догадался.
Основная идея, думаю, ясна — это более продвинутая обфускация. Как это сделать? Погуглив, я пришёл к выводу, что сделать это вполне реально и даже достаточно просто.
С шифрованием исходников всё понятно, зашифровать и/или обфусцировать файлы можно множеством способов, главное, чтобы там была «каша» и «ничего не понятно», а также всё это должно возвращаться к первоначальному виду неизвестным способом (в случае обфускации). Для приведённого здесь примера я буду использовать Python-модуль base64 для «шифрования». В некритичных случаях можно применять замечательный пакет obfuscate.
Python the Importer Protocol
Как же нам реализовать возможность импортировать модули из зашифрованных файлов? К счастью, в Python реализована система хуков при импорте, которая работает на основе Importer Protocol (PEP 302). Значит эту возможность и будем использовать. Для перехвата импортов используется словарь sys.meta_path , в котором должны храниться объекты finder/loader , реализующие Importer Protocol. Опрос этого словаря всегда происходит до того момента, как будут проверены пути в sys.path .
Для минимальной реализации протокола импорта нужно реализовать два метода: find_module и load_module . Метод find_module отвечает за поиск конкретного модуля/пакета (ведь нам нужно перехватывать импорт только своих модулей, а остальные отдавать на откуп стандартному механизму), а метод load_module , соответственно, загружает конкретный модуль только если он был «найден» в методе find_module .
Итак, вроде бы добрались до сути. Можно привести простой пример. Минимальный пример класса, реализующего Importer Protocol, подходящего для наших целей. Он будет заниматься импортом модулей «зашифрованных» base64 из обычной структуры пакетов (в данном случае для простоты мы просто «зашифровали содержимое» файлов, но никак не меняли их имена и структуру пакетов). Считаем, что расширения файлов для наших модулей будут гордо называться «.b64».
Класс импортёра
#coding=utf-8 import os import sys import imp import base64 EXT = ‘.b64’ #=============================================================================== class Base64Importer(object): «»»Служит для поиска и импорта python-модулей, кодированных в base64 Класс реализует Import Protocol (PEP 302) для возможности импортирования модулей, зашифрованных в base64 из указанного пакета. «»» #————————————————————————— def __init__(self, root_package_path): self.__modules_info = self.__collect_modules_info(root_package_path) #————————————————————————— def find_module(self, fullname, path=None): «»»Метод будет вызван при импорте модулей Если модуль с именем fullname является base64 и находится в заданной папке, данный метод вернёт экземпляр импортёра (finder), либо None, если модуль не является base64. «»» if fullname in self.__modules_info: return self return None #————————————————————————— def load_module(self, fullname): «»»Метод загружает base64 модуль Если модуль с именем fullname является base64, то метод попытается его загрузить. Возбуждает исключение ImportError в случае любой ошибки. «»» if not fullname in self.__modules_info: raise ImportError(fullname) # Для потокобезопасности imp.acquire_lock() try: mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) mod.__file__ .format(self.__class__.__name__) mod.__loader__ = self if self.is_package(fullname): mod.__path__ = [] mod.__package__ = fullname else: mod.__package__ = fullname.rpartition(‘.’)[0] src = self.get_source(fullname) try: exec src in mod.__dict__ except: del sys.modules[fullname] raise ImportError(fullname) finally: imp.release_lock() return mod #————————————————————————— def is_package(self, fullname): «»»Возвращает True если fullname является пакетом «»» return self.__modules_info[fullname][‘ispackage’] #————————————————————————— def get_source(self, fullname): «»»Возвращает исходный код модуля fullname в виде строки Метод декодирует исходные коды из base64 «»» filename = self.__modules_info[fullname][‘filename’] try: with file(filename, ‘r’) as ifile: src = base64.decodestring(ifile.read()) except IOError: src = » return src #————————————————————————— def __collect_modules_info(self, root_package_path): «»»Собирает информацию о модулях из указанного пакета «»» modules = <> p = os.path.abspath(root_package_path) dir_name = os.path.dirname(p) + os.sep for root, _, files in os.walk(p): # Информация о текущем пакете filename = os.path.join(root, ‘__init__’ + EXT) p_fullname = root.rpartition(dir_name)[2].replace(os.sep, ‘.’) modules[p_fullname] = < ‘filename’: filename, ‘ispackage’: True ># Информация о модулях в текущем пакете for f in files: if not f.endswith(EXT): continue filename = os.path.join(root, f) fullname = ‘.’.join([p_fullname, os.path.splitext(f)[0]]) modules[fullname] = < ‘filename’: filename, ‘ispackage’: False >return modules
Как это работает? Первым делом при создании экземпляра класса собирается информация о модулях нашей библиотеки, которую мы «шифруем». Затем при загрузке конкретного модуля, читается нужный «зашифрованный» файл, «расшифровывается» и импортируется с помощью средств модуля imp уже из «расшифрованной» текстовой строки. Как использовать данный класс? Очень легко. Буквально, одной строчкой включается возможность импортировать «зашифрованные» исходники нашей библиотеки, а по сути ставится хук на импорт:
sys.meta_path.append(Base64Importer(root_pkg_path))
где root_pkg_path абсолютный или относительный путь к корневому пакету нашей библиотеки. При этом, убирая данную строчку, мы можем использовать обычные исходники, если они доступны. Всё происходит абсолютно прозрачно, а все изменения происходят в одном месте.
Вот и всё, с этого момента импорт модулей из нашей библиотеки осуществляется с перехватом и «расшифровкой». Наш хук будет дёргаться при любом вызове инструкции import, и если импортируется модули нашей библиотеки, хук будет их обрабатывать и загружать, остальные импорты будут обрабатываться стандартно. Что нам и требовалось для более продвинутой обфускации.
Представленный код импортёра и установки хука можно положить уже в *.pyd файл и надеяться на то, что никто не будет его дизассемблировать в надежде понять, что мы тут наворотили. В реальном проекте можно использовать настоящее шифрование, в том числе с использованием аппаратного ключа, что должно повысить надёжность данного метода. Также изменение имён файлов и структуры пакетов может быть полезным для большего запутывания.
Заключение
В качестве заключения хочу сказать, что я противник скрывать исходники, которые нельзя просто так взять и скрыть. В данном случае я не осмелюсь обсуждать этическую сторону вопроса и нужность/полезность сокрытия Python-исходников. Тут я просто представил метод, как это можно сделать и получить какой-то результат. Естественно, это не панацея.
Python-код, действительно, невозможно скрыть полностью и от всех. Код модулей всегда можно получить с помощью интроспекции встроенными возможностями языка после их загрузки, например, из переменной sys.modules . Но это уже не так очевидно, как если бы исходники были открыты изначально.
Возможно, что всё, что тут написано и яйца выеденного не стоит — давно известные истины, либо бред сумасшедшего. Но если вышеописанное кому-то может оказаться полезным, я буду рад. Лично для меня данный опыт был полезен, хотя бы потому, что позволил лучше разобраться в мощной и гибкой системе загрузки и импортирования модулей и Importer Protocol. О тех самых штуках, которые чаще всего не требуются разработчикам для написания программ на Python. Всегда интересно узнать что-то новое.
Спасибо за внимание.
UPD 14.08.2013:
По просьбе tangro сделал минимальный проект, в котором демонстрируется описанный способ, но без настоящего шифрования (применены только некие алгоритмы обратимого преобразования).
Скачать zip-архив можно по ссылке. Нужен Python 2.7 x86 под Windows. Запускать нужно скрипт «test_main.py».
UPD 2:
И более интересный пример, в котором производятся некоторые вычисления. Здесь все импорты и вызовы функций из зашифрованных модулей скрыты в бинарном модуле. Скачать архив можно по ссылке.
- python
- защита данных
- защита исходников
- защита исходных кодов python
- python importer protocol
- obfuscation
- обфускация
Источник: habr.com
Дайте совет, как защитить код на Python?
Приложения на python изначально предполагают распространение в виде исходного кода. Собственно этим он и неудобен. Теоретически — можно откомпилировать и распространять бинарник — но это возможно только под windows.
Другой вопрос в том, что скомпилированный код можно декомпилировать… и вы все равно его не защитите…, но это уже вопрос ценности самого кода (точнее того, покроют ли трудозатраты на декомпиляцию стоимость результата)
С практической точки зрения — защита может быть только юридическая. Да… — старые и проверенные авторские права… ну а че…
Если без хардкора никак — то:
Как возможность — использование библиотек на c++ (или другом языке, кроме java), которые будут собраны и распространяться вместе с кодом и выполнять часть работы, критическую для тиражирования. Однако такое положение вещей может нарушать некоторые лицензии.
Отдельной возможностью может стоять упаковка в контейнер, такой как docker, AppImage… (их много всяких: похожих и не похожих, контейнеров и не очень) или какую-то похожую по смыслу технологию и уже в рамках контейнера распространять приложение и продумывать его защиту от тиражирования.
Источник: yandex.ru