user2430629
user2430629

Reputation:

How can I create a pyc file from a function?

I'm in a game coded in python, and I can access all modules and their functions.

I wan't to get a .pyc file of some of the larger functions so that I can put it through dePython or something similar. It would be a huge pain to read these functions manually.

Say I have Module.function, what can I do to get that function into a .pyc file?

Thanks!

Upvotes: 0

Views: 1624

Answers (2)

rocky
rocky

Reputation: 7098

The function write_pycfile() from xasm can write a pyc file. It takes an "asm" object but it needs basically a Python interpreter version and a list of code object to write. It uses functions mostly from xdis. Here is a modified version to show basically how this works:

import xdis
from xdis import magic2int
from xdis.marsh import dumps
from xdis.magics import magics
from struct import pack
from xdis.version_info import PYTHON3, version_tuple_to_str
import time

def write_pycfile(fp, code_list, timestamp=None,version_triple=xdis.PYTHON_VERSION_TRIPLE):

    version_str = version_tuple_to_str(version_triple, end=2)
    magic_bytes = magics[version_str]
    magic_int = magic2int(magic_bytes)
    fp.write(magic_bytes)

    if timestamp is None:
        timestamp = int(time.time())
     write_source_size = version_triple >= (3, 3)
    if version_triple >= (3, 7):
        if magic_int == 3393:
            fp.write(pack("I", timestamp))
            fp.write(pack("I", 0))
        else:
            # PEP 552. https://www.python.org/dev/peps/pep-0552/
            # 0 in the lowest-order bit means used old-style timestamps
            fp.write(pack("<I", 0))
            fp.write(pack("<I", timestamp))
    else:
        fp.write(pack("<I", timestamp))

    if write_source_size:
        fp.write(pack("<I", 0))  # size mod 2**32

    for co in code_list:
        try:
            co_obj = dumps(co, python_version=version_triple)
            if PYTHON3 and version_triple < (3, 0):
                co_obj = str.encode(co_obj)
                pass

            fp.write(co_obj)
        except Exception:
            pass

if PYTHON3:
    file_mode = 'wb'
else:
    file_mode = 'w'

pyc_file = "/tmp/test.pyc"
with open("/tmp/test_pyc", file_mode) as fp:
    write_pycfile(fp, [write_pycfile.__code__])
    print(f"Wrote {pyc_file}")

Now run it and disassemble:

$ python /tmp/write-pyc.py 
Wrote /tmp/test_pyc.pyc

$ pydisasm /tmp/test_pyc.pyc
# pydisasm version 4.3.2
# Python bytecode 3.6 (3379)
# Disassembled from Python 3.8.2 (default, Mar 28 2020, 12:46:55) 
# [GCC 7.5.0]
# Timestamp in code: 1587126086 (2020-04-17 08:21:26)
# Method Name:       write_pycfile
# Filename:          /tmp/write-pyc.py
# Argument count:    3
# Kw-only arguments: 0
# Number of locals:  8
# Stack size:        18
# Flags:             0x00000043 (NOFREE | NEWLOCALS | OPTIMIZED)
# First Line:        9
# Constants:
#    0: None
...
# Names:
....    
10:           0 LOAD_GLOBAL               0 (PYTHON3)
              2 POP_JUMP_IF_FALSE        10 (to 10)
...

Finally decompile using uncompyle6:

$ uncompyle6 /tmp/test_pyc.pyc
# uncompyle6 version 3.9.0a1
# Python bytecode version base 3.6 (3379)
# Decompiled from: Python 3.6.15 (default, Sep 21 2021, 05:10:37) 
# [GCC 9.3.0]
# Embedded file name: /tmp/write-pyc.py
# Compiled at: 2022-07-24 04:23:45
# Size of source mod 2**32: 1359 bytes
import xdis
from xdis import magic2int
from xdis.marsh import dumps
from xdis.magics import magics
from struct import pack
from xdis.version_info import PYTHON3, version_tuple_to_str
import time

def write_pycfile(fp, code_list, timestamp=None, version_triple=xdis.PYTHON_VERSION_TRIPLE):
    version_str = version_tuple_to_str(version_triple, end=2)
    magic_bytes = magics[version_str]
    magic_int = magic2int(magic_bytes)
    fp.write(magic_bytes)
    if timestamp is None:
        timestamp = int(time.time())
    else:
        write_source_size = version_triple >= (3, 3)
        if version_triple >= (3, 7):
            if magic_int == 3393:
                fp.write(pack('I', timestamp))
                fp.write(pack('I', 0))
            else:
                fp.write(pack('<I', 0))
                fp.write(pack('<I', timestamp))
        else:
            fp.write(pack('<I', timestamp))
    if write_source_size:
        fp.write(pack('<I', 0))
    for co in code_list:
        try:
            co_obj = dumps(co, python_version=version_triple)
            if PYTHON3:
                if version_triple < (3, 0):
                    co_obj = str.encode(co_obj)
            fp.write(co_obj)
        except Exception:
            pass
# okay decompiling /tmp/write-pyc.py

Upvotes: 3

woozyking
woozyking

Reputation: 5220

You can either import the module to have .pyc automatically generated, or if you prefer to do it in a programmatic manner, use py_compile module: http://docs.python.org/2/library/py_compile.html

Upvotes: 1

Related Questions