Pull to refresh

Comments 41

Спасибо за статью! Сам несколько раз задумывался над защитой исходников на Python, а тут узнал сразу кучу способов.
А сделайте по своей методике зашифрованный «Хеллоу ворлд!» и положите тут. Кажется мне, что хуком на вашу функцию дешифровки достать питоновский код как нефиг делать. Ключ шифрования-то локально доступен.
То есть, сделать «Хеллоу ворлд!» с бинарным модулем, в котором будет производиться расшифровка? Могу сделать, конечно, как только доберусь до подходящего компьютера.
Добрался. :)

Сейчас набросаю. Метод шифрования вам, в принципе, не важен? Вам нужен просто бинарный модуль, в котором где-то есть функция расшифровки питоньих исходников, преобразованных некоторым алгоритмом, верно?
Ага. Ну чтобы можно было попробовать достать сам питоновский код. С «хеллоу ворлд» будет не очень интересно — ясно же что искать. Напишите там что-ли решение квадратного уравнения, или сумму чисел массива.
Хорошо. Только не очень понятно, зачем, если задача — получить питоний код из модуля. Можете пока посмотреть то что есть?
Спасибо, обязательно посмотрю, отпишусь.
Попробуйте второй пример с вычислениями.
А у вас, наверное, интерпретатор Python x64? Судя по первым строчкам на скриншоте [MSC v.1500 64 bit (AMD64)], так и есть. Я собирал *.pyd-ы с x86 интерпретатором.
Довольно интересно. Хотя даже с использованием реального шифрования с аппаратным ключом такая защита сработает только от неискушенного пользователя. В памяти то все равно все расшифровано и как вы указали несложно все оттуда вытащить. Но все же решение заслуживает право на существование.
есть такая игра — Eve Online. большая ее часть написана на питоне.

ее разработчики применили именно такой подход — шифрование файлов на диске и свой импортер c расшифровкой на лету.

естественно, никого из желающих порыться в исходниках на предмет внесения нужных изменений это не останавливало. алгоритм шифрования просто и тупо восстановили из бинарника, ключи — извлекли.

еще есть такое приложение — AceStream (бывший TorrentStream). большая его часть — честно сворованный Tribbler, написаный на питоне.
его «разработчики» тоже использовали шифрование и свой импортер.

результат — абсолютно тот-же самый.
О, спасибо за информацию! Я этого не знал. Значит на моём кривом велосипеде уже кто-то успел прокатиться до меня. Результат, конечно, вполне предсказуемый. :)
Вообще-то термин «обфускация» идет гораздо дальше, чем просто нарушение форматирования исходника, удаление комментариев и замена имен функций и переменных. Вот, например, прочтите статью по ссылке: Анализ запутывающих преобразований программ. Если бы в вашем распоряжении был обфускатор, использующий методы вроде описанных в статье по ссылке — то результат, думаю, получился бы гораздо более впечатляющий, чем шифрование. Тут любая декомпиляция и автоформатирование мало помогли бы тому, кто пытался бы разобраться в работе программы.
Да, я понимаю, что термин «обфускация» достаточно ёмкий, за которым стоят сложные методы и техники запутывания кода, исследованные и доказанные математически. Но для Python мне не встречались проекты с таким уровнем методов запутывания, какой даётся в статье по вашей ссылке.
Классная фраза из статьи по ссылке:
Хотя удаление комментариев — одностороннее преобразование, их отсутствие не затрудняет сильно обратную инженерию программы, так как при обратной инженерии наличие хороших комментариев к коду программы является скорее исключением, чем правилом.
Все верно, питон мало приспособлен для сокрытия исходников. Скорее всего, если одно из главных требований — закрытые исходники, то инструмент нужно выбирать какой-то другой.
Шифрование как правило делается двухступенчатое — в бинарнике (например C/C++) и в скрипте (python).
Кроме того с открытым (для расшифровки) и закрытым (для шифрования) ключами и подписями.
Скрипт должен быть подписан приватным ключем (чтобы нельзя было изменить).
А главное: функции расшифровки могут быть вызваны только из классов (либо статики) из пространств имен тоже только зашифрованых и подписаных приватным ключем (т.н. safe area). Зная только паблик-ключ, хакеру не возможно самому создать safe area и придется ковырять бинарные модули, что не есть просто.
Такое шифрование гораздо сложнее организовать (для этого бинарно переопределяется пространство имен стандартного загрузчика, вызовы конструкторов классов и т.д. и т.п) — но и ковырять таким образом защищенный код тоже то еще удовольствие.
Тут одно из двух: либо все эти чудеса всё-таки будут на рантайме расшифровываться (а значит хук на функцию расшифровки покажет нам расшифрованный код), либо включается режим «там будет какая-то магия, не знаю как, но будет работать».
В моем примере, хук не повесить на изолированый код (safe area) из неизолированого кода (unsafe area).
Другими словами я (подписав код) могу повесить хук на код, вы не имея приватного ключа не можете…
И оно будет расшифровываться (а затем исполнятся) сразу в байт код, который нужно еще найти — утянуть. Теоретически возможно — да, но: байт код питона, расшифровывающийся на лету, ложится в памяти кусками как хочет.

Если же мы говорим про решение автора — я вас всецело поддерживаю.

PS. Вообще если говорить о полной защищенности, то и С/С++ решения вскрываются, дизассемблируются частично или полностью и т.д. Как правило любая защита может быть вскрыта, и это дело упирается только в стоимость такой работы (или потенциальной выгоды от вскрытия). Даже хардварные ключи ломают — а вы мне про магию рассказываете.
Я абсолютно не понимаю Вашего примера. Какая разница какие там у вас safe area или не safe area? Рано или поздно функция расшифровки будет запущена, этот запуск можно перехватить. Вы никак не можете мне запретить это сделать, поскольку хук будет, конечно, не в питоновском коде, а на уровне С++, или ниже — системных функций, или ещё ниже — в ядре ОС. И для человека, который ну хотя бы 2-3 раза это проделал повесить такой хук ни капли не сложно. Ваше решение с какими-то навороченными обёртками и ассиметричным шифрованием заберёт дни а то и недели на реализацию и добавит 10-15 минут возни взломщику.
Вы хоть раз отлаживали помесь компилированого кода со скриптовым байт кодом? Потому как ваши 10-15 минут очень быстро вырастают в человекодни и недели. Кроме того никто не отменял здесь стандартные средства защиты против отладки, дебага, многопоточную разверту и т.д. и т.п. Разговор шел о защите исполняемого кода питона, как такового.
Вы же просто холиварите на тему «Защита программы от вскрытия — тотал булшит». Таких холиваров прочитал в свое время предостаточно (да и поучавствовал, чего греха таить).
Смысл моего примера выводит отладку (как вы правильно написали) на другой уровень — из питона в С++ и ассемблер. Помесь же компилированого С++ кода со скриптовым байт кодом делает отладку (или вскрытие) программы очень неприятным занятием, поверте мне — плавали.
Помесь питона (яваскрипта, луа) и С++ — это просто сказка и халява. Рано или поздно функция дешифровки как-правило вызовет какое-нибудь memcpy, strcpy, sprintf, а они отлавливаются любым Апи Монитором. Если же они этого не сделают — дампается память всего процесса, если и это не катит — просто вешаются хуки на все входные функции питон-интрепретатора. В общем, я думаю-думаю что тут может быть сложного и в каком месте кому тут может помочь шифрование — и всё-равно не понимаю. Просил вон выше автора топика выложить пример — не хочет почему-то.
Да нет же, автор топика предлагает шифровать исходники — тут я полностью на вашей стороне — все как вы описали.
Имеющаяся же ввиду реализация (питон, тикль), которую я описал, шифрует байт-код (для питона то что в *.pyc или *.pyo), и блочно по методам и примитивам (т.е. не одним куском). Извиняюсь если где ввел в заблуждение — имелось ввиду именно это. Может теперь понятнее…
Нет, лучше пример: первый раз вызывается метод methA какого-то класса ClsB: байт код methA будет распакован, поправлены точки входа, зависимости и т.д. и метод будет вызван. Второй вызов methA вызывает уже «распакованый» байт код.
Полную дешифрацию можно провести только найдя «функцию» дещифрации (что как правило защищено другими способами не касающимися сабжа), кроме того нужно еще правильно склеить все это добро в один *.pyc файл чтобы затем декомпилировать в исходники питона.
А теперь представте, что вторая «дешифрация», генерация ключа расшифровки для второй «дешифрации» и т.д. и т.п. происходит в байт коде питона зашифрованном таким способом (т.е. многослойная или «рекурсивная» дешифрация).
На глаз и анализатором проходить многоуровневую дешифрацию байт кода (не исходника) сопоставимо а иногда даже много хуже чем подобное же с компилируемым кодом — человек должен как минимум дружить с ассемблером + уметь читать байт код питона (что поверьте ой как не легко).
Если же в этой «рекурсии» есть обратные взаимозависимости, много ловушек, ложные распаковки с вызовами сломаного кода и все это многопоточно — флаг вам в руки при дешефрации подобного.
Вот этот вот перпетум мобиль где-то существует живьём? Можете дать живой бинарник? Или Вы просто так, теоретически рассуждаете?
перпетум мобиль
:)
Есть, но не как модуль (встроен в архитектуру AppServer), дать к сожелению не могу, т.к. оно лицензируется (с привязкой к банку данных). Если будет время и удастся выдернуть, чтоб сделать небольшой бинарник без лицензии, отпишусь.
Метод, описанный вами, думается мне, довольно сложен в реализации. Может стать так, что система защиты получится дороже чем защищаемая ею информация.

Хотя, возможно, я просто не очень хорошо понял, чем будет лучше шифрование байт-кода в итоге. Ведь интерпретатор всё равно когда-то выполнит расшифрованный код, и этот код будет загружен в память в открытом виде, а значит станет доступен тому, кто его использует.
Метод, описанный вами, думается мне, довольно сложен в реализации
Есть такое дело, но я и не говорил, что это просто.
… и этот код будет загружен в память в открытом виде, а значит станет доступен тому, кто его использует.
Ну напрямую будет доступен только вызов метода (если он-вызов разрешен для unsafe area). Все остальное — много байт кода в памяти, который нужно собрать в одно целое, чтобы иметь что-то путьнее — найти все ссылки на references, variables и т.д., да не забывайте — методы распаковывают себя только после их вызова (т.е. нужно вызвать все и во всех возможных вариантах). Например одни приватные методы, которые выполняются только если условие X = 1, другие если 2 и т.д.
Можно — да, но сильно сомневаюсь, что это будет легкой прогулкой.
А ключ шифрования аппаратный?
В том случае был аппаратный ключ, без которого ПО даже не запускалось.
А если переписать на C/C++ не всё, а только «ноу-хау»?
Тогда бы пришлось переписать всё «ядро» проекта. Сейчас я думаю, что, возможно, это был бы разумный шаг.
Читал ранее обсуждение на Stackoverflow о защите кода на Python: How do I protect python code?
Суть та же: язык для этого не приспособлен. Интересное свойство, на мой взгляд. Да, на нём можно писать так, что невозможно будет перенести на другую OS/DE, но скрыть — очень сложно. Кроме web-приложений, пожалуй.
В копилку вариантов — с помощью PyPy транслировать RPython-код в C, и затем компилировать в бинарник.
Подробнее: doc.pypy.org/en/latest/translation.html

P.S. сам не пробовал
Так RPython по определению не Python, а сильно урезанное подмножество.
Использовать base64 encoding для шифрования это конечно оригинально :)
Это же только для примера. С таким же успехом можно было бы для примера использовать любой метод обратимого преобразования. :)
«Прячем» всё это в бинарный модуль расширения (*.pyd), чтобы никто не догадался.

… и пишем об этом на хабр!
UFO just landed and posted this here
Любой обфусцированый код можно поделить на две группы: тот, что его разберёт компьютер, и тот, что компьютеру не понять. Если компьютер его не поймёт, значит — не сможет запустить и нафиг нужен этот код. А если может запустить, то, поскольку компьютер попросту выполняет определённые инструкции, это может отследить человек и не нужна уже сама обфускация.
Sign up to leave a comment.

Articles