CensoredUsername / unrpyc

A ren'py script decompiler
Other
837 stars 149 forks source link

Issue of decompiling .rpyc file from renpy 7.1.3 #76

Closed ariffinsetya closed 5 years ago

ariffinsetya commented 5 years ago

I met a problem when i tried to decompile some .rpyc file it fails on function read_ast_from_file. Traceback (most recent call last): File "c:\Users\Ari\Downloads\Compressed\unrpyc-master\unrpyc.py", line 153, in worker no_pyexpr=args.no_pyexpr, comparable=args.comparable, translator=translator, init_offset=args.init_offset) File "c:\Users\Ari\Downloads\Compressed\unrpyc-master\unrpyc.py", line 112, in decompile_rpyc ast = read_ast_from_file(in_file) File "c:\Users\Ari\Downloads\Compressed\unrpyc-master\unrpyc.py", line 82, in read_ast_from_file raw_contents = chunks[1] KeyError: 1 It works fine with other .rpyc files that i had.

I tried to check by print some line on the function, because I'm not that familliar with python that's the limit of what i can do, but when i tried to decompile rpyc that works

read_ast
slot 1 start 46 length 11145
chunk slot done.
slot 2 start 11191 length 16688
chunk slot done.
slot 0 start 0 length 0
slot == 0 should break
read_ast end

and the file that doesn't work

read_ast
slot 1195656224 start 21318977 length 889192448
chunk slot done.
slot 4076863488 start 33554475 length 671088640
chunk slot done.
slot 4227858476 start 70 length 0
chunk slot done.
slot 0 start 2013265920 length 125679066
slot == 0 should break

do you have any idea why this happened?

what i did with the function `` def read_ast_from_file(in_file):

.rpyc files are just zlib compressed pickles of a tuple of some data and the actual AST of the file

raw_contents = in_file.read()
if raw_contents.startswith("RENPY RPC2"):
    # parse the archive structure
    print('read_ast')
    position = 10
    chunks = {}
    while True:
        slot, start, length = struct.unpack("III", raw_contents[position: position + 12])
        print("slot %s start %s length %s" % (slot,start,length))
        if slot == 0:
            print("should break")
            break
        position += 12
        chunks[slot] = raw_contents[start: start + length]
        print('chunk slot done.')
    raw_contents = chunks[1]
print('read_ast end')
raw_contents = raw_contents.decode('zlib')
data, stmts = magic.safe_loads(raw_contents, class_factory, {"_ast"})
print(stmts)
return stmts

``

CensoredUsername commented 5 years ago

Looks to me like someone modified the archive format a bit by making the header longer. By reinterpreting your data back to bytes it reads

b' DDGAME\x01\x00\x00\x005\x00\x00\x00\xf3+\x00\x00\x02\x00\x00\x00(,\x00\x00\xfcF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00x\xda\xb5}\x07'

Which suggests to me that the author replaced the header "RENPY RPC2" with "RENPY RPC2 DDGAME". By correcting for this the slot table decodes correctly.

in read_ast_from_file, replace position = 10 by position = 17 and it should decode properly.

ariffinsetya commented 5 years ago

thank you @CensoredUsername I'll try to do what you suggested

ariffinsetya commented 5 years ago

@CensoredUsername it's worked.