You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
197 lines
5.7 KiB
Python
Executable File
197 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Mode: -*- python -*-
|
|
#
|
|
# Copyright (c) 2015 by Rocky Bernstein <rb@dustyfeet.com>
|
|
#
|
|
"""
|
|
Usage: pydisassemble [OPTIONS]... FILE
|
|
|
|
Examples:
|
|
pydisassemble foo.pyc
|
|
pydisassemble foo.py
|
|
pydisassemble -o foo.pydis foo.pyc
|
|
pydisassemble -o /tmp foo.pyc
|
|
|
|
Options:
|
|
-o <path> output decompiled files to this path:
|
|
if multiple input files are decompiled, the common prefix
|
|
is stripped from these names and the remainder appended to
|
|
<path>
|
|
--help show this message
|
|
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
Usage_short = \
|
|
"pydissassemble [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
|
|
|
|
import sys, os, getopt, time, types
|
|
import os.path
|
|
import uncompyle6
|
|
|
|
def disassemble_code(version, co, out=None):
|
|
"""
|
|
diassembles 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)
|
|
|
|
# Pick up appropriate scanner
|
|
if version == 2.7:
|
|
import uncompyle6.scanners.scanner27 as scan
|
|
scanner = scan.Scanner27()
|
|
elif version == 2.6:
|
|
import uncompyle6.scanners.scanner26 as scan
|
|
scanner = scan.Scanner26()
|
|
elif version == 2.5:
|
|
import uncompyle6.scanners.scanner25 as scan
|
|
scanner = scan.Scanner25()
|
|
elif version == 3.2:
|
|
import uncompyle6.scanners.scanner32 as scan
|
|
scanner = scan.Scanner32()
|
|
elif version == 3.4:
|
|
import uncompyle6.scanners.scanner34 as scan
|
|
scanner = scan.Scanner34()
|
|
scanner.setShowAsm(True, out)
|
|
tokens, customize = scanner.disassemble(co)
|
|
|
|
|
|
def disassemble_file(filename, outstream=None, showasm=False, showast=False):
|
|
"""
|
|
disassemble Python byte-code file (.pyc)
|
|
"""
|
|
version, co = uncompyle6.load_module(filename)
|
|
if type(co) == list:
|
|
for con in co:
|
|
disassemble_code(version, con, outstream)
|
|
else:
|
|
from trepan.api import debug; debug
|
|
disassemble_code(version, co, outstream)
|
|
co = None
|
|
|
|
def disassemble_files(in_base, out_base, files, outfile=None):
|
|
"""
|
|
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
|
|
- <filename> outfile=<filename> (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
|
|
if outfile == '-':
|
|
outfile = None # use stdout
|
|
elif outfile and os.path.isdir(outfile):
|
|
out_base = outfile; outfile = None
|
|
elif outfile:
|
|
out_base = outfile; outfile = None
|
|
|
|
|
|
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, file) + '_dis'
|
|
outstream = _get_outstream(outfile)
|
|
# print(outfile, file=sys.stderr)
|
|
pass
|
|
|
|
# try to decomyple the input file
|
|
try:
|
|
disassemble_file(infile, outstream, showasm=True, showast=False)
|
|
except KeyboardInterrupt:
|
|
if outfile:
|
|
outstream.close()
|
|
os.remove(outfile)
|
|
raise
|
|
except:
|
|
if outfile:
|
|
outstream.close()
|
|
os.rename(outfile, outfile + '_failed')
|
|
else:
|
|
sys.stderr.write("\n# Can't disassemble %s\n" % infile)
|
|
import traceback
|
|
traceback.print_exc()
|
|
else: # uncompyle successfull
|
|
if outfile:
|
|
outstream.close()
|
|
if not outfile: print('\n# okay decompyling', infile)
|
|
sys.stdout.flush()
|
|
|
|
if outfile:
|
|
sys.stdout.write("\n")
|
|
sys.stdout.flush()
|
|
return
|
|
|
|
if sys.version[:3] != '2.7' and sys.version[:3] != '3.4':
|
|
print('Error: pydisassemble requires Python 2.7 or 3.4.', file=sys.stderr)
|
|
sys.exit(-1)
|
|
|
|
outfile = '-'
|
|
out_base = None
|
|
|
|
|
|
try:
|
|
opts, files = getopt.getopt(sys.argv[1:], 'ho:', ['help'])
|
|
except getopt.GetoptError as e:
|
|
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
|
sys.exit(-1)
|
|
|
|
for opt, val in opts:
|
|
if opt in ('-h', '--help'):
|
|
print(__doc__)
|
|
sys.exit(0)
|
|
elif opt == '-o':
|
|
outfile = val
|
|
else:
|
|
print(opt)
|
|
print(Usage_short)
|
|
sys.exit(1)
|
|
|
|
# argl, commonprefix works on strings, not on path parts,
|
|
# thus we must handle the case with files in 'some/classes'
|
|
# and 'some/cmds'
|
|
src_base = os.path.commonprefix(files)
|
|
if src_base[-1:] != os.sep:
|
|
src_base = os.path.dirname(src_base)
|
|
if src_base:
|
|
sb_len = len( os.path.join(src_base, '') )
|
|
files = [f[sb_len:] for f in files]
|
|
del sb_len
|
|
|
|
if outfile == '-':
|
|
outfile = None # use stdout
|
|
elif outfile and os.path.isdir(outfile):
|
|
out_base = outfile; outfile = None
|
|
elif outfile and len(files) > 1:
|
|
out_base = outfile; outfile = None
|
|
|
|
disassemble_files(src_base, out_base, files, outfile)
|