113 lines
4.0 KiB
Python
113 lines
4.0 KiB
Python
import argparse, dis, marshal, struct, sys, time, types
|
|
|
|
def show_file(fname):
|
|
f = open(fname, "rb")
|
|
magic = f.read(4)
|
|
moddate = f.read(4)
|
|
modtime = time.asctime(time.localtime(struct.unpack('I', moddate)[0]))
|
|
f.read(4) # don't know what these 4 bytes are
|
|
#print("magic %s" % (bytes_to_hex(magic)))
|
|
#print("moddate %s (%s)" % (bytes_to_hex(moddate), modtime))
|
|
code = marshal.load(f)
|
|
to_show_code = [code]
|
|
for c in to_show_code:
|
|
show_code(c, to_show_code)
|
|
|
|
def show_code(code, to_show_code):
|
|
print("code {}".format(code.co_name))
|
|
indent = ' '
|
|
print("%sflags %04x" % (indent, code.co_flags))
|
|
print("%sargcount %d" % (indent, code.co_argcount))
|
|
print("%snlocals %d" % (indent, code.co_nlocals))
|
|
print("%sstacksize %d" % (indent, code.co_stacksize))
|
|
#show_hex("code", code.co_code, indent=indent)
|
|
disassemble(code)
|
|
#print("%sconsts" % indent)
|
|
for const in code.co_consts:
|
|
if type(const) == types.CodeType:
|
|
# print(" {}code at {:#x}".format(indent, id(const)))
|
|
to_show_code.append(const)
|
|
# else:
|
|
# print(" %s%r" % (indent, const))
|
|
#print("%snames %r" % (indent, code.co_names))
|
|
#print("%svarnames %r" % (indent, code.co_varnames))
|
|
#print("%sfreevars %r" % (indent, code.co_freevars))
|
|
#print("%scellvars %r" % (indent, code.co_cellvars))
|
|
#print("%sfilename %r" % (indent, code.co_filename))
|
|
#print("%sname %r" % (indent, code.co_name))
|
|
#print("%sfirstlineno %d" % (indent, code.co_firstlineno))
|
|
#show_hex("lnotab", code.co_lnotab, indent=indent)
|
|
|
|
def show_hex(label, h, indent):
|
|
h = bytes_to_hex(h)
|
|
if len(h) < 60:
|
|
print("%s%s %s" % (indent, label, h))
|
|
else:
|
|
print("%s%s" % (indent, label))
|
|
for i in range(0, len(h), 60):
|
|
print("%s %s" % (indent, h[i:i+60]))
|
|
|
|
def bytes_to_hex(bs):
|
|
h = []
|
|
for b in bs:
|
|
h.append("{:02x}".format(b))
|
|
return ''.join(h)
|
|
|
|
# taken from python library
|
|
import opcode
|
|
def disassemble(co):
|
|
"""Disassemble a code object."""
|
|
code = co.co_code
|
|
num_bytes = len(code)
|
|
num_ops = 0
|
|
i = 0
|
|
extended_arg = 0
|
|
free = None
|
|
while i < num_bytes:
|
|
op = code[i]
|
|
print(repr(i).rjust(4), end=' ')
|
|
num_ops += 1
|
|
i = i+1
|
|
if op < opcode.HAVE_ARGUMENT:
|
|
print(opcode.opname[op])
|
|
else:
|
|
print(opcode.opname[op], end=' ')
|
|
oparg = code[i] + code[i+1]*256 + extended_arg
|
|
extended_arg = 0
|
|
i = i+2
|
|
if op == opcode.EXTENDED_ARG:
|
|
extended_arg = oparg*65536
|
|
#print(repr(oparg).rjust(5))
|
|
if op in opcode.hasconst:
|
|
if type(co.co_consts[oparg]) == types.CodeType:
|
|
print('code', co.co_consts[oparg].co_name)
|
|
else:
|
|
print(repr(co.co_consts[oparg]))
|
|
elif op in opcode.hasname:
|
|
print(co.co_names[oparg])
|
|
elif op in opcode.hasjrel:
|
|
print(repr(i + oparg))
|
|
elif op in opcode.haslocal:
|
|
print(oparg, co.co_varnames[oparg])
|
|
elif op in opcode.hascompare:
|
|
print(opcode.cmp_op[oparg])
|
|
elif op in opcode.hasfree:
|
|
if free is None:
|
|
free = co.co_cellvars + co.co_freevars
|
|
print(oparg, free[oparg])
|
|
elif op in opcode.hasnargs:
|
|
print('{}, {}'.format(code[i-2], code[i-1]))
|
|
else:
|
|
print(repr(oparg))
|
|
# accounting output for bytes per opcode
|
|
#print('{} bytes / {} opcodes = {} bytes per opcode'.format(num_bytes, num_ops, num_bytes / num_ops))
|
|
#print('{} {} {} # bpo'.format(num_bytes, num_ops, num_bytes / num_ops))
|
|
|
|
if __name__ == "__main__":
|
|
cmd_parser = argparse.ArgumentParser(description='Uncompile .pyc files')
|
|
cmd_parser.add_argument('files', nargs='+', help='input files')
|
|
args = cmd_parser.parse_args()
|
|
|
|
for fname in args.files:
|
|
show_file(fname)
|