WithSecureLabs / python-exe-unpacker

A helper script for unpacking and decompiling EXEs compiled from python code.
GNU General Public License v3.0
904 stars 336 forks source link

Unpacked .pyc files are invalid (with dirty solution) #14

Open Gowee opened 4 years ago

Gowee commented 4 years ago

When I try to decompile .pyc files generated by python-exe-unpacker, I got the following error:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/xdis/load.py", line 209, in load_module_from_file_object
    co = marshal.loads(bytecode)
ValueError: bad marshal data (unknown type code)

And those .pyc are also treated invalid by Python interpreter:

RuntimeError: Bad code object in .pyc file

After spending two hours figuring out what's wrong, I think it is valuable to note them down here even if this project seemingly has ended:

I suppose the problem resides around https://github.com/countercept/python-exe-unpacker/blob/6c88e9b28cb40615b680cefdb5f47ce4cab17833/pyinstxtractor.py#L322 where the appended header (incl. magic number, timestamp, size, etc) seems to mismatch with the .pyc format used by some newer Python versions (in my case 3.7).

To make uncompyle6 work well with malformed .pyc, dirty patch /usr/local/lib/python3.7/site-packages/xdis/load.py to make sure marshal.loads data starting from exactly 12th byte of original .pyc (e.g. utilizing BytesIO so that the fp can be read twice) helps.

appieGIT commented 4 years ago

I'm currently experiencing the same issue, and even though you gave the solution, I cannot figure out how to implement it myself. What do I need to change exactly in the load.py for it to work?

EDIT: Some information, I'm using Python 3.8 and also trying to decompile a .pyc file made with Python 3.8 I've been trying to make your solution work, but so far no luck.

Gowee commented 4 years ago

@appieGIT The dirty way aforementioned is a little tricky. Here is another way I did not try but expected to work:

Adding a new line pycFile.write(b'\0' * 4) just after the line https://github.com/countercept/python-exe-unpacker/blob/6c88e9b28cb40615b680cefdb5f47ce4cab17833/pyinstxtractor.py#L323

AlbertJanSch commented 4 years ago

@Gowee I have tried this solution just now. It does start right now, which is something, but it keeps returning a parse error when decompyling the .pyc files with uncompyle6 or decompyle3. I'm pretty sure this is still due to invalid .pyc files from the unpacker.

Is there any other solution you can think of?

Gowee commented 4 years ago

@AlbertJanSch Would you mind sharing a sample .pyc?

AlbertJanSch commented 4 years ago

@Gowee Of course. I uploaded a sample called calendar.pyc here: https://www.sendspace.com/file/id2d1f

Gowee commented 4 years ago

@AlbertJanSch I see Parse error at or near 'POP_BLOCK' instruction at offset 36. But I do not think it owes to python-exe-unpacker. And I have no idea how to fix it. Open a issue in the uncompyle6 project instead?

CopyMist commented 4 years ago

Gonna rephrase the solution in https://github.com/countercept/python-exe-unpacker/issues/14#issue-551482167 for us, newbies. If you use rocky/python-decompile3 after extracting .pyc files and get "bad marshal data", do the following:

  1. Open the file /usr/local/lib/python3.7/site-packages/xdis/load.py
  2. Find lines:
    if my_magic_int == magic_int:
    bytecode = fp.read()
    co = marshal.loads(bytecode)
  3. Replace with:
    if my_magic_int == magic_int:
    fp.seek(12)
    bytecode = fp.read()
    co = marshal.loads(bytecode)

    fp.seek(12) skips reading of the first 12 bytes and makes the decompiler work again.

Don't forget to revert the changes after you're done with decompilation. Thanks to the original solution provider!

venaxyt commented 3 years ago

It still doesn't works for me.. :( I tried all you have said until this message.

ecoop3r commented 2 years ago

@CopyMist solution worked great for me. Tyty

rkbennett commented 1 year ago

I know this is a fairly old issue, but also try skipping 16 and 8 as well. 16 worked for me in python 3.10, I've heard 8 also works potentially.