pycdc icon indicating copy to clipboard operation
pycdc copied to clipboard

Unsupported opcode: SWAP

Open mhdl1991 opened this issue 2 years ago • 16 comments

Hi, I tried using this tool to help a friend with a .pyc file that was written in Python 3.11.4 and I got an error that about the SWAP opcode being unsupported.

I found a suggestion here to try and comment out a line in ASTree.cpp to make the decompiler not stop the moment it sees an unsupported opcode, but that gives me a "bad cast" error instead.

What can I do?

mhdl1991 avatar Dec 14 '23 15:12 mhdl1991

attach your sample for the beginning :)

greenozon avatar Dec 14 '23 20:12 greenozon

attach your sample for the beginning :)

unfortunately for the sake of my friend's privacy, I don't think I can do so, sucks.

mhdl1991 avatar Dec 14 '23 21:12 mhdl1991

well, then the only option is to sit and wait... or try to do PR and fix! :)

greenozon avatar Dec 15 '23 16:12 greenozon

Source Generated with Decompyle++

File: test.pyc (Python 3.11)

def a(): Unsupported opcode: SWAP pass

WARNING: Decompyle incomplete

SWAP demo

hello-abson avatar Dec 31 '23 03:12 hello-abson

test.zip

def a(): for dir in "": path = dir return path pass dis.dis(a)

26 0 RESUME 0

27 2 LOAD_CONST 1 ('') 4 GET_ITER 6 FOR_ITER 7 (to 22) 8 STORE_FAST 0 (dir)

28 10 LOAD_FAST 0 (dir) 12 STORE_FAST 1 (path)

29 14 LOAD_FAST 1 (path) 16 SWAP 2 18 POP_TOP 20 RETURN_VALUE

30 >> 22 LOAD_CONST 0 (None) 24 RETURN_VALUE

SWAP demo

hello-abson avatar Dec 31 '23 03:12 hello-abson

I solved it myself and modified the source code to support the directive

hello-abson avatar Jan 01 '24 10:01 hello-abson

great news! how about creating PR for it? :)

greenozon avatar Jan 01 '24 10:01 greenozon

Let swap press the stack,
stack.push(nullptr);

I've fixed about 10 py3.11 commands myself so far. Is currently solving the try except instruction, card for a long time, do not understand how to repair

try: pass except: raise try: pass except: print("123")

hello-abson avatar Jan 01 '24 12:01 hello-abson

Hmm but why push NULL to the TOS (top of stack)?

the standard python doc says something else about how exactly SWAP opcode behaves:

image

greenozon avatar Jan 01 '24 12:01 greenozon

void swap(int index)
{
    if (m_ptr > -1)
    {
        PycRef<ASTNode> node = top();
        if (node) {
			m_stack[m_ptr] = m_stack[index - 1];
			m_stack[index - 1] = node;
        }
    }
}

I tried to write according to the document, and found that it did not show the expected results, so I gave up, and directly pressed into the empty stack to parse normally.

hello-abson avatar Jan 01 '24 16:01 hello-abson

OK, your impl looks valid but why do you push extra val (NULL) to the stack? it definitely violates the opcode mechanics...

one could agree with pushing NULL when the opcode is COPY (similar to SWAP)... any ideas?

greenozon avatar Jan 01 '24 19:01 greenozon

I'm still working on the parsing of try, help me

try: pass except: raise try: pass except: print("123")

hello-abson avatar Jan 02 '24 10:01 hello-abson

Python 3.11 uses totally different scheme known as "zero-cost" exception handling. Before 3.11, exceptions were handled by a runtime opcodes present in bytecode...

I guess you have to start parsing/using the exception table present in pyc structure but it's a bit complicated as you might have nested exceptions/etc

greenozon avatar Jan 02 '24 11:01 greenozon

I guess it is good idea to read some theory - it is inside Python sources in teh following path: Python-3.11.6.tar\Python-3.11.6\Objects\exception_handling_notes.txt

greenozon avatar Jan 02 '24 11:01 greenozon

https://github.com/zrax/pycdc/issues/409

I saw in other posts that you have solved the problem, can you tell me how to solve it? Share the code

def a(): try: print("123") finally: print("finally")

hello-abson avatar Jan 04 '24 06:01 hello-abson

Please help me to solve this PR - https://github.com/zrax/pycdc/pull/411 collaboration to the rescue!

greenozon avatar Jan 04 '24 08:01 greenozon

Duplicate #452

zrax avatar Feb 21 '24 22:02 zrax