Skip to content

Commit

Permalink
use game's own rpyc loading function inside un.rpyc and add un.rpy to…
Browse files Browse the repository at this point in the history
… prevent rpyc modification shenanigans.
  • Loading branch information
CensoredUsername committed Nov 19, 2021
1 parent 77e01d1 commit 62b14f8
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 19 deletions.
85 changes: 81 additions & 4 deletions un.rpyc/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import argparse
import os, sys
import minimize
import base64
from os import path

parser = argparse.ArgumentParser(description="Pack unpryc into un.rpyc which can be ran from inside renpy")
Expand Down Expand Up @@ -52,13 +53,13 @@
except ImportError:
exit("Could not import pickleast. Are you sure it's in pythons module search path?")

def Module(name, filename, munge_globals=True):
def Module(name, filename, munge_globals=True, retval=False):
with open(filename, "rb" if p.PY2 else "r") as f:
code = f.read()
if args.minimize:
# in modules only locals are worth optimizing
code = minimize.minimize(code, True, args.obfuscate and munge_globals, args.obfuscate, args.obfuscate)
return p.Module(name, code)
return p.Module(name, code, retval=retval)

def Exec(code):
if args.minimize:
Expand Down Expand Up @@ -95,7 +96,7 @@ def Exec(code):
files = listdirfiles()
exec '''
import os, sys, renpy
import os, sys, renpy, zlib
sys.init_offset = renpy.version_tuple >= (6, 99, 10, 1224)
sys.files = []
for (dir, fn) in files:
Expand All @@ -107,7 +108,8 @@ def Exec(code):
elif (dir, fn[:-1]) not in files:
abspath = os.path.join(dir, fn) if dir else os.path.join(basepath, fn)
with renpy.loader.load(fn) as file:
sys.files.append((abspath, fn, dir, file.read()))
bindata = renpy.game.script.read_rpyc_data(file, 1)
sys.files.append((abspath, fn, dir, bindata))
''' in globals()
_0 # util
Expand Down Expand Up @@ -178,6 +180,73 @@ def Exec(code):
Module("unrpyc", path.join(pack_folder, "unrpyc-compile.py"))
))

rpy_one = p.GetItem(p.Sequence(
Module("util", path.join(base_folder, "decompiler/util.py")),
Module("magic", path.join(base_folder, "decompiler/magic.py"), False),
Module("codegen", path.join(base_folder, "decompiler/codegen.py")),
), "magic")

rpy_two = p.GetItem(p.Sequence(
Module("testcasedecompiler", path.join(base_folder, "decompiler/testcasedecompiler.py")),
Module("screendecompiler", path.join(base_folder, "decompiler/screendecompiler.py")),
Module("sl2decompiler", path.join(base_folder, "decompiler/sl2decompiler.py")),
Module("decompiler", path.join(base_folder, "decompiler/__init__.py")),
Module("unrpyc", path.join(pack_folder, "unrpyc-compile.py"))
), "unrpyc")

rpy_base = """
init python early hide:
# Set up the namespace
import os
import os.path
import sys
import renpy
import renpy.loader
import base64
import pickle
import zlib
basepath = os.path.join(os.getcwd(), "game")
files = renpy.loader.listdirfiles()
sys.init_offset = renpy.version_tuple >= (6, 99, 10, 1224)
sys.files = []
for (dir, fn) in files:
if fn.endswith((".rpyc", ".rpymc")):
if dir and dir.endswith("common"):
continue
elif fn == "un.rpyc":
continue
elif (dir, fn[:-1]) not in files:
abspath = os.path.join(dir, fn) if dir else os.path.join(basepath, fn)
with renpy.loader.load(fn) as file:
bindata = renpy.game.script.read_rpyc_data(file, 1)
sys.files.append((abspath, fn, dir, bindata))
# ???
magic = pickle.loads(base64.b64decode({}))
renpy_modules = sys.modules.copy()
for i in renpy_modules:
if b"renpy" in i and not b"renpy.execution" in i:
sys.modules.pop(i)
renpy_loader = sys.meta_path.pop()
magic.fake_package(b"renpy")
magic.FakeModule(b"astdump")
magic.FakeModule(b"translate")
# ?????????
unrpyc = pickle.loads(base64.b64decode({}))
unrpyc.decompile_game()
magic.remove_fake_package(b"renpy")
sys.modules.update(renpy_modules)
sys.meta_path.append(renpy_loader)
"""

unrpyc = zlib.compress(
p.optimize(
Expand All @@ -191,13 +260,21 @@ def Exec(code):
protocol),
9)

unrpy = rpy_base.format(
repr(base64.b64encode(p.optimize(p.dumps(rpy_one, protocol), protocol))),
repr(base64.b64encode(p.optimize(p.dumps(rpy_two, protocol), protocol)))
)


with open(path.join(pack_folder, "un.rpyc"), "wb") as f:
f.write(unrpyc)

with open(path.join(pack_folder, "bytecode.rpyb"), "wb") as f:
f.write(bytecoderpyb)

with open(path.join(pack_folder, "un.rpy"), "wb") as f:
f.write(unrpy)

if args.debug:
print("File length = {0}".format(len(unrpyc)))

Expand Down
15 changes: 0 additions & 15 deletions un.rpyc/unrpyc-compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,6 @@ def __new__(cls, name):
factory = magic.FakeClassFactory((PyExpr, PyCode, RevertableList, RevertableDict, RevertableSet, Sentinel), magic.FakeStrict)

def read_ast_from_file(raw_contents):
# .rpyc files are just zlib compressed pickles of a tuple of some data and the actual AST of the file
if raw_contents.startswith("RENPY RPC2"):
# parse the archive structure
position = 10
chunks = {}
while True:
slot, start, length = struct.unpack("III", raw_contents[position: position + 12])
if slot == 0:
break
position += 12

chunks[slot] = raw_contents[start: start + length]

raw_contents = chunks[1]
raw_contents = raw_contents.decode('zlib')
data, stmts = magic.safe_loads(raw_contents, factory, {"_ast", "collections"})
return stmts

Expand Down

0 comments on commit 62b14f8

Please sign in to comment.