You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
scripts->bin. Add pydisassemble to list of scripts to install
This commit is contained in:
196
bin/pydissassemble
Executable file
196
bin/pydissassemble
Executable file
@@ -0,0 +1,196 @@
|
||||
#!/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)
|
199
bin/uncompyle6
Executable file
199
bin/uncompyle6
Executable file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python
|
||||
# Mode: -*- python -*-
|
||||
#
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
#
|
||||
"""
|
||||
Usage: uncompyle6 [OPTIONS]... [ FILE | DIR]...
|
||||
|
||||
Examples:
|
||||
uncompyle6 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
|
||||
uncompyle6 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis
|
||||
uncompyle6 -o /tmp /usr/lib/python1.5 # decompile whole library
|
||||
|
||||
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>
|
||||
uncompyle6 -o /tmp bla/fasel.pyc bla/foo.pyc
|
||||
-> /tmp/fasel.pyc_dis, /tmp/foo.pyc_dis
|
||||
uncompyle6 -o /tmp bla/fasel.pyc bar/foo.pyc
|
||||
-> /tmp/bla/fasel.pyc_dis, /tmp/bar/foo.pyc_dis
|
||||
uncompyle6 -o /tmp /usr/lib/python1.5
|
||||
-> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
|
||||
-c <file> attempts a disassembly after compiling <file>
|
||||
-d do not print timestamps
|
||||
-p <integer> use <integer> number of processes
|
||||
-r recurse directories looking for .pyc and .pyo files
|
||||
--verify compare generated source with input byte-code
|
||||
(requires -o)
|
||||
--help show this message
|
||||
|
||||
Debugging Options:
|
||||
--showasm -a include byte-code (disables --verify)
|
||||
--showast -t include AST (abstract syntax tree) (disables --verify)
|
||||
|
||||
Extensions of generated files:
|
||||
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
|
||||
+ '_unverified' successfully decompile but --verify failed
|
||||
+ '_failed' decompile failed (contact author for enhancement)
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
Usage_short = \
|
||||
"uncompyle6 [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
|
||||
|
||||
import sys, os, getopt
|
||||
import os.path
|
||||
from uncompyle6 import main, status_msg, verify
|
||||
import time
|
||||
|
||||
if sys.version[:3] != '2.7' and sys.version[:3] != '3.4':
|
||||
print('Error: uncompyle6 requires Python 2.7 or 3.4.', file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
showasm = showast = do_verify = recurse_dirs = False
|
||||
numproc = 0
|
||||
outfile = '-'
|
||||
out_base = None
|
||||
codes = []
|
||||
timestamp = True
|
||||
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
|
||||
|
||||
try:
|
||||
opts, files = getopt.getopt(sys.argv[1:], 'hatdro:c:p:',
|
||||
['help', 'verify', 'showast', 'showasm'])
|
||||
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 == '--verify':
|
||||
do_verify = True
|
||||
elif opt in ('--showasm', '-a'):
|
||||
showasm = True
|
||||
do_verify = False
|
||||
elif opt in ('--showast', '-t'):
|
||||
showast = True
|
||||
do_verify = False
|
||||
elif opt == '-o':
|
||||
outfile = val
|
||||
elif opt == '-d':
|
||||
timestamp = False
|
||||
elif opt == '-c':
|
||||
codes.append(val)
|
||||
elif opt == '-p':
|
||||
numproc = int(val)
|
||||
elif opt == '-r':
|
||||
recurse_dirs = True
|
||||
else:
|
||||
print(opt)
|
||||
print(Usage_short)
|
||||
sys.exit(1)
|
||||
|
||||
# expand directory if specified
|
||||
if recurse_dirs:
|
||||
expanded_files = []
|
||||
for f in files:
|
||||
if os.path.isdir(f):
|
||||
for root, _, dir_files in os.walk(f):
|
||||
for df in dir_files:
|
||||
if df.endswith('.pyc') or df.endswith('.pyo'):
|
||||
expanded_files.append(os.path.join(root, df))
|
||||
files = expanded_files
|
||||
|
||||
# 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
|
||||
|
||||
if timestamp:
|
||||
print(time.strftime(timestampfmt))
|
||||
if numproc <= 1:
|
||||
try:
|
||||
result = main(src_base, out_base, files, codes, outfile, showasm,
|
||||
showast, do_verify)
|
||||
if len(files) > 1:
|
||||
mess = status_msg(do_verify, *result)
|
||||
print('# ' + mess)
|
||||
pass
|
||||
except (KeyboardInterrupt):
|
||||
pass
|
||||
except verify.VerifyCmpError:
|
||||
raise
|
||||
else:
|
||||
from multiprocessing import Process, Queue
|
||||
|
||||
try:
|
||||
from Queue import Empty
|
||||
except ImportError:
|
||||
from Queue import Empty
|
||||
|
||||
fqueue = Queue(len(files)+numproc)
|
||||
for f in files:
|
||||
fqueue.put(f)
|
||||
for i in range(numproc):
|
||||
fqueue.put(None)
|
||||
|
||||
rqueue = Queue(numproc)
|
||||
|
||||
def process_func():
|
||||
try:
|
||||
(tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0)
|
||||
while 1:
|
||||
f = fqueue.get()
|
||||
if f is None:
|
||||
break
|
||||
(t, o, f, v) = \
|
||||
main(src_base, out_base, [f], codes, outfile, showasm, showast, do_verify)
|
||||
tot_files += t
|
||||
okay_files += o
|
||||
failed_files += f
|
||||
verify_failed_files += v
|
||||
except (Empty, KeyboardInterrupt):
|
||||
pass
|
||||
rqueue.put((tot_files, okay_files, failed_files, verify_failed_files))
|
||||
rqueue.close()
|
||||
|
||||
try:
|
||||
procs = [Process(target=process_func) for i in range(numproc)]
|
||||
for p in procs:
|
||||
p.start()
|
||||
for p in procs:
|
||||
p.join()
|
||||
try:
|
||||
(tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0)
|
||||
while True:
|
||||
(t, o, f, v) = rqueue.get(False)
|
||||
tot_files += t
|
||||
okay_files += o
|
||||
failed_files += f
|
||||
verify_failed_files += v
|
||||
except Empty:
|
||||
pass
|
||||
print('# decompiled %i files: %i okay, %i failed, %i verify failed' %
|
||||
(tot_files, okay_files, failed_files, verify_failed_files))
|
||||
except (KeyboardInterrupt, OSError):
|
||||
pass
|
||||
|
||||
|
||||
if timestamp:
|
||||
print(time.strftime(timestampfmt))
|
Reference in New Issue
Block a user