0

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!

2
  • Which version of Python is this? Is it using a modified interpreter? Anyway, you may find the dis module helpful. I beleive there are also Python decompilers that can operate on in memory functions. Commented Jul 19, 2013 at 3:37
  • I believe it's python 2.7.3. The dis module is helpful, but It's annoying to read the output. That's why I want to put it in a .pyc file and then put it through something like dePython Commented Jul 19, 2013 at 3:47

2 Answers 2

3

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 
Sign up to request clarification or add additional context in comments.

Comments

1

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

4 Comments

The problem with that is: I don't know where the .pyc file is saved if it is saved at all.
The .pyc files are generated and stored alongside the source .py files, for auto-generated case. py_compile might offer more options for you to explicitly specify where to output the .pyc.
I don't have the .py files :/ The game is not open-source or anything. It's all compiled and inaccessible. All I can do is import the module, and use the functions inside. I can't access the .py files, I can't run the module to save a .pyc. I can't do anything like that. All I can do is import Module Module.function() dis.dis(Module.function) etc.
@LifelessMango I see. Then I'm afraid you should be rephrasing your question a bit better and tag it with reverse-engineering maybe?