from __future__ import print_function import os, sys, types from uncompyle6.disas import check_object_path from uncompyle6 import verify from uncompyle6.semantics import pysource from uncompyle6.scanner import get_scanner from uncompyle6.load import load_module # FIXME: remove duplicate code from deparse_code def uncompyle(version, co, out=None, showasm=False, showast=False): """ disassembles and deparses a given code block 'co' """ assert isinstance(co, types.CodeType) # store final output stream for case of error real_out = out or sys.stdout print('# Python %s' % version, file=real_out) if co.co_filename: print('# Embedded file name: %s' % co.co_filename, file=real_out) scanner = get_scanner(version) tokens, customize = scanner.disassemble(co) if showasm: for t in tokens: print(t, file=real_out) print(file=out) # Build AST from disassembly. walk = pysource.Walker(version, out, scanner, showast=showast) try: ast = walk.build_ast(tokens, customize) except pysource.ParserError as e : # parser failed, dump disassembly print(e, file=real_out) raise del tokens # save memory # convert leading '__doc__ = "..." into doc string assert ast == 'stmts' try: if ast[0][0] == pysource.ASSIGN_DOC_STRING(co.co_consts[0]): walk.print_docstring('', co.co_consts[0]) del ast[0] if ast[-1] == pysource.RETURN_NONE: ast.pop() # remove last node # todo: if empty, add 'pass' except: pass walk.mod_globs = pysource.find_globals(ast, set()) walk.gen_source(ast, customize) for g in walk.mod_globs: walk.write('global %s ## Warning: Unused global' % g) if walk.ERROR: raise walk.ERROR def uncompyle_file(filename, outstream=None, showasm=False, showast=False): """ decompile Python byte-code file (.pyc) """ check_object_path(filename) version, magic_int, co = load_module(filename) if type(co) == list: for con in co: uncompyle(version, con, outstream, showasm, showast) else: uncompyle(version, co, outstream, showasm, showast) co = None def main(in_base, out_base, files, codes, outfile=None, showasm=False, showast=False, do_verify=False): ''' in_base base directory for input files out_base base directory for output files (ignored when files list of filenames to be uncompyled (relative to src_base) outfile write output to this filename (overwrites out_base) For redirecting output to - outfile= (out_base is ignored) - files below out_base out_base=... - stdout out_base=None, outfile=None ''' def _get_outstream(outfile): dir = os.path.dirname(outfile) failed_file = outfile + '_failed' if os.path.exists(failed_file): os.remove(failed_file) try: os.makedirs(dir) except OSError: pass return open(outfile, 'w') of = outfile tot_files = okay_files = failed_files = verify_failed_files = 0 # for code in codes: # version = sys.version[:3] # "2.5" # with open(code, "r") as f: # co = compile(f.read(), "", "exec") # uncompyle(sys.version[:3], co, sys.stdout, showasm=showasm, showast=showast) for filename in files: infile = os.path.join(in_base, filename) # print (infile, file=sys.stderr) if of: # outfile was given as parameter outstream = _get_outstream(outfile) elif out_base is None: outstream = sys.stdout else: outfile = os.path.join(out_base, filename) + '_dis' outstream = _get_outstream(outfile) # print(outfile, file=sys.stderr) # try to decomyple the input file try: uncompyle_file(infile, outstream, showasm, showast) tot_files += 1 except ValueError as e: sys.stderr.write("\n# %s" % e) failed_files += 1 except KeyboardInterrupt: if outfile: outstream.close() os.remove(outfile) sys.stderr.write("\nLast file: %s " % (infile)) raise except: failed_files += 1 if outfile: outstream.close() os.rename(outfile, outfile + '_failed') else: sys.stderr.write("\n# Can't uncompyle %s\n" % infile) else: # uncompyle successfull if outfile: outstream.close() if do_verify: try: msg = verify.compare_code_with_srcfile(infile, outfile) if not outfile: if not msg: print('\n# okay decompyling %s' % infile) okay_files += 1 else: print('\n# %s\n\t%s', infile, msg) except verify.VerifyCmpError as e: verify_failed_files += 1 os.rename(outfile, outfile + '_unverified') if not outfile: print("### Error Verifiying %s" % filename, file=sys.stderr) print(e, file=sys.stderr) else: okay_files += 1 if not outfile: mess = '\n# okay decompyling' # mem_usage = __memUsage() print(mess, infile) if outfile: sys.stdout.write("%s\r" % status_msg(do_verify, tot_files, okay_files, failed_files, verify_failed_files)) sys.stdout.flush() if outfile: sys.stdout.write("\n") sys.stdout.flush() return (tot_files, okay_files, failed_files, verify_failed_files) # ---- main ---- if sys.platform.startswith('linux') and os.uname()[2][:2] in ['2.', '3.', '4.']: def __memUsage(): mi = open('/proc/self/stat', 'r') mu = mi.readline().split()[22] mi.close() return int(mu) / 1000000 else: def __memUsage(): return '' def status_msg(do_verify, tot_files, okay_files, failed_files, verify_failed_files): if tot_files == 1: if failed_files: return "decompile failed" elif verify_failed_files: return "decompile verify failed" else: return "Successfully decompiled file" pass pass mess = "decompiled %i files: %i okay, %i failed" % (tot_files, okay_files, failed_files) if do_verify: mess += (", %i verify failed" % verify_failed_files) return mess