Import pickle не работает

Ошибка импорта файла Pickle

Я пытался загрузить файл Python Pickle, используя следующую функцию Python:

но я продолжаю сталкиваться со следующей ошибкой:

Он жалуется на модуль с именем sysid, который вызывается в файле pickle.py. Если я импортирую pickle вместо (cPickle as pickle), я получаю следующее более подробное сообщение об ошибке:

Кто-нибудь знает, что может быть причиной ошибки?

1 ответ

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

Недостатком является то, что файлы pickle на самом деле не могут передаваться между различными средами Python (и не могут надежно передаваться даже между различными файлами или модулями Python). Когда дело доходит до загрузки класса или объекта, файл pickle использует ту же структуру импорта / пространство имен, которое присутствовало при создании pickle. Это означает, что файлы Pickle, созданные из того же модуля, который определяет класс, могут быть перезагружены только в этом же модуле (если вы не импортируете этот класс вручную в ваше глобальное пространство имен).

Насколько мы знаем sysid_functions был подмодулем какого-то другого пакета, который вы не установили. Даже если он установлен, вы, вероятно, сможете загрузить файл pickle, только если вам удалось настроить модуль globals() так же, как и модуль, создавший файл pickle.

Источник

Модуль pickle

Модуль pickle реализует мощный алгоритм сериализации и десериализации объектов Python. «Pickling» — процесс преобразования объекта Python в поток байтов, а «unpickling» — обратная операция, в результате которой поток байтов преобразуется обратно в Python-объект. Так как поток байтов легко можно записать в файл, модуль pickle широко применяется для сохранения и загрузки сложных объектов в Python.

Модуль pickle предоставляет следующие функции для удобства сохранения/загрузки объектов:

pickle.dump(obj, file, protocol=None, *, fix_imports=True) — записывает сериализованный объект в файл. Дополнительный аргумент protocol указывает используемый протокол. По умолчанию равен 3 и именно он рекомендован для использования в Python 3 (несмотря на то, что в Python 3.4 добавили протокол версии 4 с некоторыми оптимизациями). В любом случае, записывать и загружать надо с одним и тем же протоколом.

pickle.dumps(obj, protocol=None, *, fix_imports=True) — возвращает сериализованный объект. Впоследствии вы его можете использовать как угодно.

pickle.load(file, *, fix_imports=True, encoding=»ASCII», errors=»strict») — загружает объект из файла.

pickle.loads(bytes_object, *, fix_imports=True, encoding=»ASCII», errors=»strict») — загружает объект из потока байт.

Модуль pickle также определяет несколько исключений:

  • pickle.PickleError
    • pickle.PicklingError — случились проблемы с сериализацией объекта.
    • pickle.UnpicklingError — случились проблемы с десериализацией объекта.

Этих функций вполне достаточно для сохранения и загрузки встроенных типов данных.

Источник

Опасные pickles — вредоносная сериализация в Python

Panta rhei и вот уже приближается запуск обновленного курса «Web-разработчик на Python» и у нас остался ещё материал, который мы нашли сильно небезынтересным и коим хотим поделиться с вами.

Чем опасны pickles?

Эти соленые огурчики крайне опасны. Я даже не знаю, как объяснить, насколько. Просто поверь мне. Это важно, понимаешь?

“Explosive Disorder” Pan Telare

Прежде чем с головой погрузиться в опкод, поговорим об основах. В стандартной библиотеке Python есть модуль под названием pickle (в переводе “соленый огурчик” или просто ”консервация”), который используется для сериализации и десериализации объектов. Только называется это не сериализация/десериализация, а pickling/unpickling (дословно — “консервация/расконсервация”).

Как человек, которого до сих пор мучают кошмары после использования Boost Serialization в C++, могу сказать, что консервация отличная. Что бы вы в нее не кинули, она продолжает Просто Работать. И не только с builtin типами — в большинстве случаев, можно сериализовать свои классы, без необходимости писать сериализационные консервирующие методы. Даже с такими объектами, как рекурсивные структуры данных (которые бы вызвали падение при использовании похожего marshal модуля), проблем не возникает.

Читайте также:  Телевизор кансай как настроить

Приведем быстрый пример для тех, кто еще не знакомым с модулем pickle:

Этого достаточно в большинстве случаев. Консервация действительно классная… но где-то в глубине скрывается тьма.

В одной из первых строк pickle модуля написано:
Внимание: Модуль pickle не защищен от ошибочных и вредоносных данных. Никогда не делайте расконсервацию данных из ненадежного и неавторизованного источника.

Я много раз перечитал это предупреждение и часто задавался вопросом, что же могут собой представлять вредоносные данные. И недавно я решил, что настало время это узнать. И не зря.
Мой квест по созданию вредоносных данных помог мне узнать многое о работе протокола pickle, обнаружить классные методы дебаггинга консервации и найти парочку дерзких комментариев в исходном коде Python. Если продолжите чтение, то получите те же преимущества (и скоро тоже начнете отправлять людям свои вредоносные консервационные файлы). Предупреждение: будут технические детали, единственный пререквизит — базовое знание Python. Но и поверхностное знание ассемблера не помешает.

Ненастоящая Pickle Bomb

Я начал с чтения документации pickle модуля, в надежде найти подсказки, как стать элитным хакером, и наткнулся для строку:
Модуль pickletools содержит инструменты для анализа потоков данных, сгенерированных консервацией. Исходный код pickletools содержит обширные комментарии об опкодах, используемых pickle протоколами.

Опкоды? Я совсем не рассчитывал, что имплементация pickle будет такой:

Но я также не ожидал, что она определит собственный низкоуровневый язык. К счастью, вторая часть строки говорит правду — pickletools модули очень помогают понять, как работаю протоколы. Плюс, комментарии в коде оказались очень забавными.

К примеру, поставим вопрос, на какой версии протокола нам нужно сфокусироваться. В Python 3.6 их в общей сложности пять. Они пронумерованы от 0 до 4. Протокол 0 — очевидный выбор, потому что он назван “читабельным” в документации, а исходный код pickletools предлагает дополнительную информацию:

Опкоды pickle никогда не исчезают, даже когда появляются новые способы делать что-нибудь. Репертуар PM только растет со временем… “Вздутие опкода” — не тонкий намек, а источник изнуряющих сложностей.

Оказывается, каждый новый протокол является суперсетом предыдущего. Даже если не брать во внимание, что протокол 0 “читабельный” (это неважно, потому что мы декомпилируем инструкции), он также содержит наименьшее количество возможных опкодов. Что идеально подходит, если цель — понять, как создаются вредоносные pickle файлы.

Если вы запутались с опкодами, не переживайте. Сейчас мы вернемся к Python, а после я в деталях объясню, как опкоды соотносятся с кодом Python. Создадим простой класс Python без опкодов.

Методы __setstate__() и __getstate__() используются в модуле pickle для сериализации и десериализации классов. Часто не нужно определять их самостоятельно, потому что имплементации по умолчанию просто сериализуют __dict__ инстанса. Как видим, я прямо определил их здесь, чтобы спрятать небольшой сюрприз в момент десериализации объекта Bomb.

Проверим, работает ли код десериализации с сюрпризом. Мы законсервируем и расконсервируем объект с помощью:

Точно по плану! Есть только одна проблема: если мы попытаемся десериализовать строку pickled_bomp в контексте, где Bomb не определена, ничего не выйдет. Вместо этого появится ошибка:

Оказывается, мы можем запустить наш кастомный метод __setstate__() , только если у расконсервирующего контекста уже есть доступ к коду с нашим вредоносным print выражением. А если у нас уже есть доступ к коду, запущенному жертвой, зачем вообще заморачиваться с pickle? Мы можем просто написать вредоносный код в любом другом методе, которым воспользуется жертва. И это верно — я просто хотел наглядно продемонстрировать.

В конце концов, совсем не напрасно подозревать, что Pyton может поддерживать консервационный байт-код для метода десериализации объекта. К примеру, модуль marshal может сериализовать методы, и многие альтернативы pickle: marshmallow, dill, и pyro, также поддерживают сериализацию функции.

Тем не менее, зловещее предупреждение в документации pickle говорит не об этом. Нужно погрузиться чуть глубже, чтобы узнать опасности десериализации.

Настало время попытаться понять, как на самом деле работает консервация. Начнем с рассмотрения объекта из предыдущего раздела — pickled_bomb.

Читайте также:  Биржа турдом не работает

Постойте… мы же использовали протокол 0? Разве это “читабельно”?

Но ничего страшного, в исходном коде pickletools мы должны найти “обширные комментарии об опкодах, используемых pickle протоколами”. Они должны помочь нам разобраться в проблеме!

Я отчаянно документирую это детально — прочитайте pickle код полностью, чтобы найти все частные случаи.

— комментарий в исходном коде pickletools

Боже. Во что мы вписались?

Шутки в сторону, исходный код pickle tools действительно отлично прокомментирован. И сами инструменты не менее полезны. Например, есть метод для разборки pickle под названием pickletools.dis(). Он поможет перевести наш pickle на более понятный язык.

Для разборки нашей строки pickled_bomb, просто запустим следующее:

Если вы имели дело с языками вроде x86, Dalvik, CLR, то все вышеописанное может показаться знакомым. Но даже если не имели — не беда, разберем все по шагам. Сейчас достаточно знать, что заглавные слова вроде GLOBAL, PUT, и MARK — опкоды, и инструкции, которые интерпретируются почти как функции в высокоуровневых языках. Все что правее — аргументы этих функций, а левее показано, как они были зашифрованы в оригинальной “читабельной” строке.

Но перед тем как начать пошаговый разбор, представим еще одну полезную вещь из pickletools: pickletools.optimize(). Этот метод удаляет неиспользуемые опкоды из pickle. На выходе получается упрощенный, но аналогичный pickle. Можем разобрать оптимизированную версию pickled_bomb, запустив следующее:

И получим упрощенную версию серии инструкций:

Можно заметить, что от оригинала это отличается только отсутствием всех PUT опкодов. Что оставляет нам 10 инструкционных шагов, которые нужно понять. Вскоре, мы рассмотрим их по отдельности и вручную “разберем” в Python код.

Во время расконсервации опкоды обычно интерпретируются сущностью под названием Pickle Machine (PM). Каждый pickle — программа, запущенная на PM, примерно как скомпилированный Java код запускается на Java Virtual Machine (JVM). Чтобы разобрать наш pickle код, нужно разобраться в работе PM.

В PM есть две области для хранения данных и взаимодействия с ними: memo и stack. Memo предназначен для долговременного хранения, и похож на словарь Python, сопоставляющий целые числа и объекты. Stack подобен списку Python, с которым взаимодействуют многие операции, добавляя и вытаскивая вещи. Мы можем эмулировать эти области данных Python следующим образом:

Во время расконсервации PM читает pickle программу и последовательно выполняет каждую инструкцию. Он завершается всякий раз, когда достигает опкода STOP; любой объект, находящийся наверху стека, является финальным результатом расконсервации. Используя наши сэмулированные memo и stack хранилища, попробуем перевести наш pickle на Python… инструкция за инструкцией.

  • GLOBAL пушит класс и функцию в стэк, передавая модуль и имя в качестве аргументов. Заметим, что сообщение немного вводит в заблуждение, потому что в Python 3 copy_reg был переименован в copyreg.

    MARK пушит в стэк особый markobject, чтобы впоследствии мы могли использовать его для уточнения части стэка. Мы воспользуемся строкой “MARK” для репрезентации markobject.

    GLOBAL опять. Но в этот раз с модулем __main__, поэтому нам не нужно проводить import.
    GLOBAL опять. И нам не нужно явно импортить object.

    NONE просто пушит None в стэк.

    TUPLE немного сложнее. Помните, как мы раньше добавляли “MARK” в стэк? Эта операция переместит все из стэка после “MARK” в кортеж. После этого она удалит “MARK” и заменит его на кортеж.

    REDUCE убирает последние две вещи из стэка. После этого она вызывает предпоследний объект, используя позиционное расширение последней вещи, и полученный результат размещает в стэке. Словами сложно объяснить, но в коде все понятно

    UNICODE просто пушит строку юникода в стэк (очень неплохую строку юникода, к слову!)

    BUILD убирает последний объект из стэка и затем передает его в качестве аргумента в __setstate__() новой последней вещью стэка

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

Фух, мы закончили! Не уверен, что наш код особенно Python’ный… но он эмулирует работу PM. Можно заметить, что мы ни разу не воспользовались memo. Помните все те PUT опкоды, которые были удалены при pickletools.optimize()? В них могло происходить взаимодействие с momo, но в нашем простом примере это не понадобилось.

Читайте также:  Манто айо 80w не работает

Попробуем упростить код, чтобы наглядно показать его работу. По факту, кроме перемешивания данных, происходит только три операции: импорт _reconstructor в инструкции 1, вызов _reconstructor в инструкции 7 и вызов __setstate__() в инструкции 9. Если мысленно представить перемешивание данных, то можно выразить все тремя строками Python.

Взгляд изнутри на исходный код copyreg._reconstructor() выявляет, что мы просто вызываем object.__new__(Bomb). Пользуясь этим знанием, можем упростить все до двух строк.

Поздравляю, вы только что декомпилировали pickle!

Настоящая Pickle Бомба

Я не pickle эксперт, но уже представляю в общих чертах, как сконструировать вредоносный pickle. Можно использовать опкод GLOBAL для импорта любой функции — os.system и __builtin__.eval кажутся подходящими кандидатами. А после воспользуемся REDUCE для его выполнения с произвольным аргументом. Но только… погодите, что это?

Если не isinstance(callable, тип), REDUCE не будет ругаться только в том случае, когда callable был зарегистрирован в словаре safe_constructors модуля copyreg, или у callable есть волшебный атрибут __safe_for_unpickling__ с истинным значением. Не знаю, почему так происходит, но я видел достаточное количество жалоб .

Подмигиваем в ответ. Похоже документация pickletools подсказывает, что только разрешенные callable могут быть выполнены REDUCE. На мгновение это заставило меня поволноваться, но поиск “safe_constuctors” быстро помог найти PEP 307 из 2003.

В прошлых версиях Python расконсервация имела “проверку безопасности” на отдельных операциях, отказываясь вызывать функции или конструкторы, которые не были отмечены “безопасными для расконсервации” за наличие атрибута __safe_for_unpickling__ равного 1, или регистрации в глобальном регистре copy_reg.safe_constructors.

Эта функция создает ложное ощущение безопасности: никто никогда не проводил необходимую обширную проверку кода, чтобы доказать, что расконсервация pickle из ненадежных источников не может вызвать нежелательный код. Фактически, баги в модуле pickle.py Python 2.2 позволяют легко обойти эти меры предосторожности.

Мы твердо убеждены, что при использовании интернета, лучше знать, что ваш протокол небезопасен, чем доверять безопасности протокола, чья имплементация не была досконально проверена. Даже высококачественная имплементация популярных протоколов зачастую содержит ошибки; без больших временных вложений имплементация pickle в Python просто не может дать гарантий. Поэтому, начиная с версии Python 2.3, все проверки безопасности расконсервации официально исключены и заменены на предупреждение:
Предупреждение: Не расконсервируйте данные, полученные из ненадеждых и не прошедших проверку источников.

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

Чтобы превратить это в настоящий pickle, нужно заменить каждый опкод на соответствующий ASCII код: c для GLOBAL, ( для MARK, V для UNICODE, t для TUPLE, R для REDUCE, и. для STOP. Заметим, что это те же самые значения, что были прописаны слева от опкодов в выводе pickletools.dis() ранее. Аргументы анализируются после каждого опкода с учетом комбинации позиции и ограничения новой строки. Каждый аргумент расположен либо сразу после соответствующего опкода, либо после предыдущего аргумента, и читается непрерывно до тех пор, пока не будет найден символ новой строки. Перевод в машинный код pickle дает следующее:

Наконееец-то, мы можем это проверить:

Знаю, что у вас нет причин мне верить, но это действительно сработало с первого раза.
Легко понять, что кто-нибудь может с легкостью придумать более вредоносный аргумент для eval(). PM можно заставить делать буквально все что угодно, что может выполнить код Python, включая системные команды os.system().

Все хорошее когда-нибудь заканчивается

Я планировал узнать, как сделать опасный pickle, но случайно в процессе понял, как pickle’ы работают. Признаюсь, мне понравилось копаться в этой Pickle Machine. Исходный код pickletools ощутимо помог, и я рекомендую его, если вам интересно узнать больше о pickle протоколе и PM.

Как всегда ждём пожелания и вопросы, которые можно задать тут или лично Илье Лебедеву на Дне открытых дверей.

Источник

Оцените статью