Python-проект в EXE-файл и обфускация кода
В этом посте познакомимся с инструментами, позволяющими превратить Python-проект в exe-файл, а также узнаем, что такое обфускация кода.
Реклама
Работая на фрилансе, помимо выполнения своей задачи, не стоит забывать и о сохранности своего труда. Что я имею в виду? Вам скорее всего известны риски фриланса, когда исполнитель отправляет работу заказчику, а тот пропадает не оплачивая её. Не будем вдаваться в специфику фриланса, а поговорим о безопасных способах передачи "демо версии".
Существует несколько способов безопасно продемонстрировать выполненную работу заказчику, минимизируя риски неоплаты:
- Демонстрация результата через удаленный доступ.
Самый простой и очевидный способ - запустить проект на своём компьютере или сервере, предоставив доступ заказчику только к результату (например, запустить Telegram-бота или веб-сервис). - Сборка исполняемого файла (exe)
Такой способ подойдёт, если проект не требует дополнительных зависимостей, таких как база данных (которую можно запустить и у себя, прописав подключение к ней) или, например, когда задача представляет собой GUI-приложение. - Сборка exe-файла с обфускацией кода
Этот метод похож на предыдущий, но включает дополнительный уровень защиты: обфускация усложняет чтение и анализ исходного кода. Хотя она не делает код полностью неуязвимым для опытных разработчиков, ее использование может существенно затруднить обратную разработку (reverse engeneering
).
Если первый способ понятен интуитивно, то второй и третий требуют более детального объяснения.
Что такое сборка исполняемого файла (exe)?
Сборка в исполняемый (exe
) файл представляет собой преобразование Python-проекта в файл/файлы, пригодные для запуска в окружении, где может не быть предустановлен интерпретатор Python.
Мне известны два способа сборки:
- Запаковка средствами PyInstaller — это инструмент для упаковки Python-приложений в исполняемые файлы. Он собирает Python-скрипт вместе с необходимыми библиотеками, модулями и интерпретатором Python в один файл или папку, удобные для распространения.
Сайт проекта.
Пример команды для созданияexe
-файла:
pyinstaller --onefile script.py
- Компиляция средствами Nuitka — это компилятор для языка Python, который преобразует исходный код Python в машинный код или промежуточный байт-код (в зависимости от настроек), что ускоряет выполнение программ и делает их независимыми от интерпретатора Python. Nuitka также может генерировать исполняемые файлы, которые могут быть запущены без необходимости наличия интерпретатора Python.
Сайт проекта.
Пример команды для компиляции с использованием Nuitka:
nuitka --standalone script.py
Каждый из способов обладает своими плюсами и минусами, о которых мы поговорим далее в статье.
Что такое обфускация кода?
Обфускация кода — это процесс изменения исходного кода программы таким образом, чтобы затруднить понимание для человека, при сохранении работоспособности программы.
Обычно обфускация применяется для защиты программного обеспечения от несанкционированного доступа и копирования, а также для усложнения процесса обратной разработки (reverse engeneering
).
Стоит отметить, что обфускация не защищает полностью от реверс-инженеринга. Однако, для полной защиты нужно обладать специально обученными людьми. В контексте фриланса, это отличная защита.
Помимо ручного способа, о котором говорить не будем, поскольку это весьма трудозатратное занятие, существует несколько инструментов, вот некоторые из них:
- PyArmor — это инструмент для защиты Python скриптов от копирования. Он работает путем шифрования исходного кода и его динамической расшифровки во время выполнения.
Репозиторий библиотеки.
Пример команды для обфускации с использованием PyArmor:
pyarmor pack script.py
- Nuitka — несмотря на то что Nuitka это компилятор Python, он также может использоваться для обфускации кода. Он компилирует исходный код в промежуточный байт-код или машинный код, что затрудняет его понимание.
Сайт проекта.
- Python Minifier — это инструмент для минификации и обфускации Python кода. Он удаляет пробелы, комментарии и документацию из исходного кода, а также может переименовывать переменные и функции в случайные названия.
Репозиторий библиотеки.
Пример команды для минификации с использованием Python Minifier:
pyminify example.py --output example.min.py
Что будет рассматриваться в посте?
Вариантов для применения и комбинирования методов достаточно много, но мы остановимся на двух:
- Рассмотрим процесс обфускации кода библиотекой
Python Minifier
, а затем запакуем его в исполняемый файл библиотекойPyInstaller
. - Скомпилируем исполняемый файл библиотекой
Nuitka
.
Оба варианты будут независимы и выбор будет за вами.
Для большей наглядности мы проведем описанные выше процессы на пррограмме MPF Tools Converter
, которую я написал приятелю год назад (написал не лучшим образом, но сейчас не об этом). Для тех, кто хочет ознакомиться: репозиторий на GitHub.
Приступим!
⚠️Обратите внимание!⚠️
Результат выполнения сборки зависит от используемой системы! То есть: запустив сборку на Linux, вы получите исполняемый файл для Linux, соответственно, запустив сборку на Windows, вы получите исполняемый файл для Windows.
Рабочего способа, собрать из Linux версию для Windows, не прибегая к виртуальной машине я не нашёл. Если вам известен способ, пожалуйста, напишите в комментариях!
Python Minifier + PyInstaller
С Python Minifier
всё понятно по предыдущему блоку: он выполняет минификацию — процесс оптимизации, при котором содержимое файлов сокращается за счёт удаления пробелов, комментариев и других необязательных элементов. Кроме того, инструмент переименовывает переменные и изменяет структуру кода, что делает его сложным для чтения, но позволяет уменьшить размер файлов и повысить их эффективность.
Применение Python Minifier
Для начала нужно установить библиотеку. Сделаем это выполнив команду:
pip install python-minifier
По умолчанию он использует "полный набор" минификации, но, если вам что-то не нужно, то в документации указан подробный перечень аргументов запуска: https://dflook.github.io/python-minifier/command_usage.html
В моём случае, проект состоит из main.py-файла
и пакета app
с ещё двумя .py-файлами
. Следовательно их нужно или обработать вручную каждый отдельно, либо использовать команду для пакетного изменения.
pyminify main.py app/ --in-place
Вот так выглядел main.py
до применения:
А вот так после:
Другие файлы:
Пример может показаться не очень наглядным и читаемым, т.к. используется многое из библиотеки Flet и на более независимом от библиотек коде картина будет лучше. Тем не менее, разбор всего этого займёт много времени и сил.
Можно убедиться, что всё работает корректно запустив программу:
Применение PyInstaller
Прежде чем приступим, давайте познакомимся с PyInstaller
поближе.
Основные особенности PyInstaller:
- Автоматическое обнаружение и включение всех необходимых зависимостей и модулей.
- Поддержка различных платформ (Windows, macOS, Linux).
- Возможность создания оконных приложений с использованием библиотек GUI.
Как работает PyInstaller?
PyInstaller анализирует код Python, определяет зависимости (модули и библиотеки) и включает их в итоговый пакет.
Результат может быть представлен:
- одним большим исполняемым файлом (режим
onefile
). - папкой с отдельным исполняемым файлом и всеми зависимостями (режим
onedir
).
PyInstaller использует системный интерпретатор Python, добавляя его в пакет, а затем запускает программу как обычный Python-скрипт внутри скомпилированного окружения.
Установка и применение
Для установки библиотеки, необходимо выполнить следующую команду:
pip install -U pyinstaller
У PyInstaller много параметров запуска, позволяющих настроить сборку исполняемого файла "под себя". Полный перечень параметров доступен в документации: https://pyinstaller.org/en/stable/usage.html
В моём случае команда будет выглядеть так:
pyinstaller --noconfirm --onefile --noconsole \
--name 'MPF Converting Tool' --icon './favicon.ico' \
--add-data './app:app/' \
'main.py'
Разберём параметры:
--noconfirm
- Этот параметр отключает запрос подтверждения при перезаписи существующих файлов сборки. PyInstaller без дополнительных вопросов заменит ранее созданные файлы.--onefile
- Объединяет все зависимости и саму программу в один исполняемый файл. Это удобно для распространения, так как итоговый файл не требует дополнительных папок или библиотек рядом.--noconsole
- Убирает отображение консольного окна при запуске программы. Полезно для графических приложений, где консоль не нужна.--name 'MPF Converting Tool'
- Задаёт имя для создаваемого исполняемого файла. В данном случае выходной файл будет называтьсяMPF Converting Tool.exe
(на Windows) или простоMPF Converting Tool
(на Linux/MacOS).--icon './favicon.ico'
- Указывает путь к файлу иконки, которая будет использоваться для созданного исполняемого файла. Здесь используетсяfavicon.ico
.--add-data './app:app/'
- Добавляет дополнительные данные в сборку. В данном случае папка./app
добавляется и будет доступна в приложении какapp/
. Формат записи:source:destination
.'main.py'
- Основной файл программы, который PyInstaller должен обработать и собрать в исполняемый файл.
Результат выполнения
Выполнив команду начнётся процесс сборки. Если всё прошло успешно, вы увидите в терминале запись "27927 INFO: Building EXE from EXE-00.toc completed successfully.".
В директории из которой был вызван PyInstaller появится две директории: build
и dist
.
В директории build
содержатся временные файлы, необходимые для сборки, а в директории dist
итоговый исполняемый файл.
Откроем директорию dist
в файловом менеджере. Там находится исполняемый файл.
Запустим его, чтобы удостовериться, что всё работает:
Я получил ошибку, что не может найти необходимые файлы. Это нормально, просто положим их рядом:
И запустим снова:
Слетели размеры окна приложения, но это, скорее всего связанно с запуском на Linux. В остальном программа запустилась и работает корректно.
Итог по использованию Python Minifier + PyInstaller
Процесс достаточно прост и не вызывает трудностей.
Python Minifier неплохо справляется со своей задачей - усложнить чтение кода. Отмечу ещё раз, что это не панацея и есть более продвинутые инструменты, например, тот же PyArmor, который ещё и шифрует файлы, но он куда более сложен в применении и в разрезе задач для фриланса, это кажется излишним.
Что касается PyInstaller, то это живая классика. Обратите внимание на размер итогового файла: почти 40 мегабайт! И это всего три .py-файла
+ несколько библиотек. Всё дело в том, что внутри файла также находится и интерпретатор Python.
Компилируем проект в Nuitka
Теперь давайте разберёмся, как можно скомпилировать проект с помощью Nuitka и чем этот процесс отличается от использования PyInstaller и обфускации с Python Minifier.
Основные особенности Nuitka:
- Высокая производительность, так как код компилируется в машинный код или промежуточный байт-код.
- Поддержка большинства функций языка Python и некоторых дополнений.
- Возможность создания независимых исполняемых файлов.
Процесс работы Nuitka:
- Анализ исходного кода Python для определения всех зависимостей и возможных оптимизаций.
- Компиляция исходного кода в промежуточный байт-код или машинный код.
- Создание исполняемого файла, который может быть запущен без необходимости наличия Python.
Как Nuitka отличается от PyInstaller?
Характеристика | PyInstaller | Nuitka |
---|---|---|
Тип упаковки | Собирает Python-скрипт с интерпретатором и модулями | Компилирует Python-код в бинарный файл через C |
Размер файла | Обычно больше, так как включает интерпретатор | Меньше, поскольку компилирует код |
Производительность | Производительность такая же, как у исходного кода | Выше за счёт оптимизации и машинного кода |
Совместимость | Поддерживает большинство библиотек Python | Может возникнуть сложность с модулями C API |
Цель | Быстрая упаковка для распространения | Улучшение производительности и компиляция |
Как видно из таблицы, Nuitka
имеет много преимуществ перед PyInstaller
, в том числе повышение скорости работы и уменьшение размера исполняемого файла.
Нужно ли использовать Python Minifier перед Nuitka?
В отличие от сценария с PyInstaller
, дополнительная минификация или обфускация кода перед использованием Nuitka
обычно не требуется. Сам процесс компиляции уже затрудняет восстановление читаемого кода. Если же вы хотите усилить защиту, Nuitka
предлагает платные и бесплатные версии:
- Бесплатная версия включает базовый функционал компиляции Python-кода в машинный, обеспечивая защиту от простого чтения исходного кода.
- Платная версия (Commercial Edition) добавляет расширенные возможности, такие как встроенная обфускация, шифрование строковых литералов и дополнительная оптимизация для повышения безопасности.
Если вам нужны улучшенные средства защиты, имеет смысл рассмотреть использование коммерческой версии, которая интегрирует функции обфускации прямо в процесс компиляции.
Установка Nuitka
Для установки необходимо выполнить команду:
pip install -U nuitka
Для работы, Nuitka требует наличие в системе C-компилятора.
- Для Windows:
- Visual Studio 2022 или выше. Не путать с VisualStudio Code / VSCode! Используйте Английский язык в Visual Studio, чтобы Nuitka корректнее фильтровала вывод компилятора.
- Компилятор
MinGW64
. Используется с параметром--mingw64
.Nuitka
предложит скачать его, если не найдёт Visual Studio. - Некоторые другие компиляторы, предоставляемые пакетами Visual Studio и MinGW64.
- Для Lunux: используется
GCC-компилятор
или установленныйclang
. - Для macOS: используется системный Clang. Чтобы получить к нему доступ, установите
XCode
изApple Store
.
Запуск сборки
Для запуска выполним следующую команду:
nuitka --standalone --onefile --output-filename="MPF Converting Tool" \
--windows-disable-console --windows-icon-from-ico=./favicon.ico \
main.py
Разберём параметры:
--standalone
- Собирает приложение с зависимостями, создавая автономный исполняемый файл.--onefile
- Упаковывает всё в один файл.--output-filename="MPF Converting Tool"
- Указывает имя выходного файла. (Для Windows в конец необходимо добавить.exe
!)--windows-disable-console
- Отключает консольное окно для графических приложений.--windows-icon-from-ico=./favicon.ico
- Добавляет иконку для исполняемого файла.main.py
- указывает точку входа в проект.
Результат выполнения
Процесс компиляции может быть неторопливым и занять какое-то время. В процессе будет создано три директории: main.build
, main.dist
и main.onefile-build
, содержащие временные файлы.
По окончании в корне проекта появится нужный нам исполняемый файл:
Точно также запустим, чтобы убедиться в работоспособности:
Итог по использованию Nuitka
При беглом знакомстве, Nuitka
кажется отличным инструментом для создания исполняемых файлов, включаем в себя компиляцию и усложнение "разбора" файла на исходный код.
По результату, мы получили файл размером 31 мегабайт, что меньше результата PyInstaller
на 10 мегабайт, но не кажется выдающимся успехом. Вероятно, зависит от проекта.
Заключение
Было интересно в процессе написания поста пробовать разное и разбираться, как всё это работает.
Что лучше — PyInstaller или Nuitka?
- Если ваша основная цель — быстро собрать исполняемый файл для передачи заказчику, PyInstaller подойдёт лучше: он проще в использовании и не требует дополнительных инструментов.
- Если вы хотите повысить производительность и затруднить разбор программы, выбирайте Nuitka.
Всё описанное выше, не призыв к действию, а лишь описание способов защиты своего труда. Способов на самом деле куда больше и каждый следующий сложнее и комплектнее предыдущего. Выбор какой именно применять, остаётся за вами.
Все статьи