Собрать файл .py из байт-кода - PullRequest
0 голосов
/ 17 февраля 2019

Постановка проблемы

У меня есть файл (без расширения) с некоторыми красиво отформатированными кодами операций Python, который я хотел бы собрать в исходный файл .py (или как можно ближе к нему)).

Проблема воссоздания

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

a = 1
b = 2
print(a+b)

Запустив python3 -m dis test.py, я получу следующий вывод:

  1       0 LOAD_CONST               0 (1)
          2 STORE_NAME               0 (a)

  2       4 LOAD_CONST               1 (2)
          6 STORE_NAME               1 (b)

  3       8 LOAD_NAME                2 (print)
         10 LOAD_NAME                0 (a)
         12 LOAD_NAME                1 (b)
         14 BINARY_ADD
         16 CALL_FUNCTION            1
         18 POP_TOP
         20 LOAD_CONST               2 (None)
         22 RETURN_VALUE

Я хотел бы восстановить оригиналtest.py файл из этого вывода.

Что я пробовал

Я уже пытался запустить uncompyle6 на выходе, но он выдает ошибку со следующимсообщение:

ImportError: Unknown magic number 8224 in test.pyc

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

Кто-то задал подобный вопрос здесь давным-давно: Повторная сборка байт-кода Python с исходным кодом? Предлагаемый ответ устарел, но даже после обновлений текущий ответ должен использовать uncompyle6, но я не могу заставить это работать.

1 Ответ

0 голосов
/ 06 апреля 2019

Существует некоторая путаница относительно того, что делает uncompyle6 .Он начинается с Python bytecode или, точнее, «wordcode», если это Python 3.6 или выше.В качестве альтернативы, он часто используется для декомпиляции скомпилированного Python-файла, который содержит байт-код.

Судя по тому, что вы показываете выше, я считаю, что вы хотите начать с представления text байт-кодасоздается дизассемблером, зависящим от версии, который поставляется (и полностью работает только с) версией, на которой работает Python.

Вот причина, по которой вы получаете это странное сообщение «Ошибка импорта» выше от uncompyle6 .Он смотрит на начало текстового файла, который вы странным образом называете скомпилированным Python-файлом.Этот файл начинается со строки в кодировке ASCII "1", а uncompyle6 интерпретирует это в соответствии с определенным форматом для скомпилированного файла Python , где начало файла содержит некую строку версии в кодировке PythonТехнически называемый «магическим числом».

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

Это в моем проекте github python-xasm .

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

Однако xasm в настоящее время нуждается в немного большей помощи, чемчто у тебя выше.В частности, он не будет угадывать по именам кодов операций, к какой версии Python они могут принадлежать.Сопоставить имена кодов операций с приемлемыми версиями Python еще сложнее, чем вы думаете.Если вы видите LOAD_CONST, вам также нужно подумать, занимает ли инструкция 2 байта или 3. Если 2, то это Python 3.6 и выше, иначе Python <3.6.И если это не достаточно сложно, некоторые версии Python изменяют значение кода операции для определенного имени кода операции!Поэтому возможно, что вы не сможете точно определить <em>, из какого интерпретатора Python происходит какая-то сборка.Но я предполагаю, что вам все равно, пока все, что вы придумаете, будет последовательным.

Итак, с учетом вышеизложенного, теперь вернемся к решению вашего вопроса.

Сначала создайте настоящий байт-код.Вы можете сделать это следующим образом

import py_compile 
py_compile.compile("/tmp/test.py", "/tmp/test.pyc", 'exec')

Теперь вместо использования встроенного дизассемблера Python используйте кросс-версию дизассемблера, который я написал, и который поставляется с xdis , который называется pydisasm и используйте опцию --asm, которая выведет сборку в xasm -дружественном способе:

$ pydisasm --asm 
# pydisasm version 4.0.0-git
# Python bytecode 3.6 (3379)
# Disassembled from Python 3.6.5 (default, Aug 12 2018, 16:37:27)
# [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.2)]
# Timestamp in code: 1554492841 (2019-04-05 15:34:01)
# Source code size mod 2**32: 23 bytes

# Method Name:       <module>
# Filename:          exec
# Argument count:    0
# Kw-only arguments: 0
# Number of locals:  0
# Stack size:        3
# Flags:             0x00000040 (NOFREE)
# First Line:        1
# Constants:
#    0: 1
#    1: 2
#    2: None
# Names:
#    0: a
#    1: b
#    2: print
  1:
            LOAD_CONST           (1)
            STORE_NAME           (a)

  2:
            LOAD_CONST           (2)
            STORE_NAME           (b)

  3:
            LOAD_NAME            (print)
            LOAD_NAME            (a)
            LOAD_NAME            (b)
            BINARY_ADD
            CALL_FUNCTION        1
            POP_TOP
            LOAD_CONST           (None)
            RETURN_VALUE

Обратите внимание на всю дополнительную информацию в комментариях вверхуфайл, который содержит некоторые действительно загадочные вещи, такие как «размер стека» и «флаги».Это и большинство других вещей должны храниться в файле байт-кода Python.

Сохраните это в файл, и , затем , вы можете собрать его в байт-код.А потом декомпилируй это.

$ ./xasm/xasm_cli.py /tmp/test.pyasm
Wrote /tmp/test.pyc
$ uncompyle6 /tmp/test.pyc
# uncompyle6 version 3.2.6
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.5 (default, Aug 12 2018, 16:37:27)
# [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.2)]
# Embedded file name: exec
# Compiled at: 2019-04-05 15:34:01
# Size of source mod 2**32: 23 bytes
a = 1
b = 2
print(a + b)
# okay decompiling /tmp/test.pyc

Я выступил с молниеносной речью на Pycon2018 в Медельине, Колумбия, по этому поводу.Извините, что вы пропустили это, но вы можете найти видео здесь http://rocky.github.io/pycon2018-light.co

Здесь показано, как:

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

Конечно, вы не можете декомпилировать это, потому что - это нелегко Python, который подражает так близко - он был изменен вручную.

Наконец, похоже,Вас также интересует, как связаны байт-код и исходный код.Поэтому я упомяну, что uncompyle6 имеет параметры --tree и еще более подробный --grammar, который покажет шаги, предпринятые для восстановления Python из байт-кода Python.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...