Last active
December 23, 2015 01:19
-
-
Save maliubiao/6559941 to your computer and use it in GitHub Desktop.
pyc disassembler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
"""globals""" | |
source = [""] | |
"""dis.py""" | |
"""Disassembler of Python byte code into mnemonics.""" | |
import sys | |
import types | |
from opcode import * | |
from opcode import __all__ as _opcodes_all | |
del _opcodes_all | |
_have_code = (types.MethodType, types.FunctionType, types.CodeType, | |
types.ClassType, type) | |
def dis(x=None): | |
"""Disassemble classes, methods, functions, or code. | |
With no argument, disassemble the last traceback. | |
""" | |
if x is None: | |
distb() | |
return | |
if isinstance(x, types.InstanceType): | |
x = x.__class__ | |
if hasattr(x, 'im_func'): | |
x = x.im_func | |
if hasattr(x, 'func_code'): | |
x = x.func_code | |
if hasattr(x, '__dict__'): | |
items = x.__dict__.items() | |
items.sort() | |
for name, x1 in items: | |
if isinstance(x1, _have_code): | |
print "Disassembly of %s:" % name | |
try: | |
dis(x1) | |
except TypeError, msg: | |
print "Sorry:", msg | |
elif hasattr(x, 'co_code'): | |
disassemble(x) | |
elif isinstance(x, str): | |
disassemble_string(x) | |
else: | |
raise TypeError, \ | |
"don't know how to disassemble %s objects" % \ | |
type(x).__name__ | |
def distb(tb=None): | |
"""Disassemble a traceback (default: last traceback).""" | |
if tb is None: | |
try: | |
tb = sys.last_traceback | |
except AttributeError: | |
raise RuntimeError, "no last traceback to disassemble" | |
while tb.tb_next: tb = tb.tb_next | |
disassemble(tb.tb_frame.f_code, tb.tb_lasti) | |
def disassemble(co, lasti=-1): | |
"""Disassemble a code object.""" | |
code = co.co_code | |
labels = findlabels(code) | |
linestarts = dict(findlinestarts(co)) | |
n = len(code) | |
i = 0 | |
extended_arg = 0 | |
free = None | |
while i < n: | |
c = code[i] | |
op = ord(c) | |
if i in linestarts: | |
if i > 0: | |
if len(source) > 1: | |
print "\033[94m%s\033[0m" % source[linestarts[i]] | |
print "%3d" % linestarts[i], | |
else: | |
print ' ', | |
if i == lasti: print '-->', | |
else: print ' ', | |
if i in labels: print '>>', | |
else: print ' ', | |
print repr(i).rjust(4), | |
print opname[op].ljust(20), | |
i = i+1 | |
if op >= HAVE_ARGUMENT: | |
oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg | |
extended_arg = 0 | |
i = i+2 | |
if op == EXTENDED_ARG: | |
extended_arg = oparg*65536L | |
print repr(oparg).rjust(5), | |
if op in hasconst: | |
print '(' + repr(co.co_consts[oparg]) + ')', | |
elif op in hasname: | |
print '(' + co.co_names[oparg] + ')', | |
elif op in hasjrel: | |
print '(to ' + repr(i + oparg) + ')', | |
elif op in haslocal: | |
print '(' + co.co_varnames[oparg] + ')', | |
elif op in hascompare: | |
print '(' + cmp_op[oparg] + ')', | |
elif op in hasfree: | |
if free is None: | |
free = co.co_cellvars + co.co_freevars | |
print '(' + free[oparg] + ')', | |
def disassemble_string(code, lasti=-1, varnames=None, names=None, | |
constants=None): | |
labels = findlabels(code) | |
n = len(code) | |
i = 0 | |
while i < n: | |
c = code[i] | |
op = ord(c) | |
if i == lasti: print '-->', | |
else: print ' ', | |
if i in labels: print '>>', | |
else: print ' ', | |
print repr(i).rjust(4), | |
print opname[op].ljust(15), | |
i = i+1 | |
if op >= HAVE_ARGUMENT: | |
oparg = ord(code[i]) + ord(code[i+1])*256 | |
i = i+2 | |
print repr(oparg).rjust(5), | |
if op in hasconst: | |
if constants: | |
print '(' + repr(constants[oparg]) + ')', | |
else: | |
print '(%d)'%oparg, | |
elif op in hasname: | |
if names is not None: | |
print '(' + names[oparg] + ')', | |
else: | |
print '(%d)'%oparg, | |
elif op in hasjrel: | |
print '(to ' + repr(i + oparg) + ')', | |
elif op in haslocal: | |
if varnames: | |
print '(' + varnames[oparg] + ')', | |
else: | |
print '(%d)' % oparg, | |
elif op in hascompare: | |
print '(' + cmp_op[oparg] + ')', | |
disco = disassemble # XXX For backwards compatibility | |
def findlabels(code): | |
"""Detect all offsets in a byte code which are jump targets. | |
Return the list of offsets. | |
""" | |
labels = [] | |
n = len(code) | |
i = 0 | |
while i < n: | |
c = code[i] | |
op = ord(c) | |
i = i+1 | |
if op >= HAVE_ARGUMENT: | |
oparg = ord(code[i]) + ord(code[i+1])*256 | |
i = i+2 | |
label = -1 | |
if op in hasjrel: | |
label = i+oparg | |
elif op in hasjabs: | |
label = oparg | |
if label >= 0: | |
if label not in labels: | |
labels.append(label) | |
return labels | |
def findlinestarts(code): | |
"""Find the offsets in a byte code which are start of lines in the source. | |
Generate pairs (offset, lineno) as described in Python/compile.c. | |
""" | |
byte_increments = [ord(c) for c in code.co_lnotab[0::2]] | |
line_increments = [ord(c) for c in code.co_lnotab[1::2]] | |
lastlineno = None | |
lineno = code.co_firstlineno | |
addr = 0 | |
for byte_incr, line_incr in zip(byte_increments, line_increments): | |
if byte_incr: | |
if lineno != lastlineno: | |
yield (addr, lineno) | |
lastlineno = lineno | |
addr += byte_incr | |
lineno += line_incr | |
if lineno != lastlineno: | |
yield (addr, lineno) | |
"""readpyc.py""" | |
import os | |
import sys | |
import marshal | |
def _f(): pass | |
CodeType = type(_f.func_code) | |
def allco(co): | |
objects = [co] | |
current = 0 | |
while True: | |
length = len(objects) | |
for code in objects[current:]: | |
for const in code.co_consts: | |
if isinstance(const, CodeType): | |
objects.append(const) | |
if len(objects) == length: | |
break | |
else: | |
current += length | |
return objects | |
if __name__ == "__main__": | |
if len(sys.argv) < 1: | |
print("usage:readpyc.py yourfile.pyc") | |
exit(0) | |
name = sys.argv[1] | |
_lines = open(name[:-1], "r").readlines() | |
if os.path.exists(name[:-1]): | |
source.extend([x.lstrip() for x in _lines]) | |
pycfile = open(name, "r+b") | |
pycfile.seek(8, os.SEEK_CUR) | |
co = marshal.loads(pycfile.read()) | |
for code in allco(co): | |
print("\033[92m%s\033[0m" % code) | |
dis(code) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment