#!/usr/bin/env python # This file is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall. # # This file is part of the Python-on-a-Chip program. # Python-on-a-Chip is free software: you can redistribute it and/or modify # it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1. # # Python-on-a-Chip is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # A copy of the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1 # is seen in the file COPYING in this directory. """ Dismantles a .py file by compiling it into a code object and recursively dismantling and disassembling the code object. """ __usage__ = """Usage: dismantle.py sourcefilename.py """ ## @file # @copybrief dismantle ## @package dismantle # @brief Dismantles a .py file by compiling it # into a code object and recursively dismantling # and disassembling the code object. STRINGTOOLONG = 32 import dis, sys, types, py_compile, time def dismantle_file(fn): """Dismantles the .py file, fn. Returns the root code object. """ #create a code object f = open(fn) source = f.read() f.close() return dismantle(source, fn) def dismantle(source, fn=""): # If no filename given, just dismantle source, skip magic and ignore if fn == "": magic = 0 ignore = 0 fn = "fn" pyc = "" else: # Compile to .pyc and open py_compile.compile(fn) f = open(fn + 'c','rb') pyc = f.read() f.close() # Check for magic number magic = int((ord(pyc[0]) ) | (ord(pyc[1]) << 8) | (ord(pyc[2]) << 16) | (ord(pyc[3]) << 24)) # Grab the next 4 bytes (don't know what they do) ignore = int((ord(pyc[4]) ) | (ord(pyc[5]) << 8) | (ord(pyc[6]) << 16) | (ord(pyc[7]) << 24)) code = compile(source, fn, "exec") # Print header print "BEGIN DISMANTLE" print "date: \t", time.ctime(time.time()) print "src file: \t", fn print "src file size: \t", len(source), "bytes" print "pyc file size: \t", len(pyc), "bytes" print print "magic: \t0x%08x" % magic print "ignore: \t0x%08x" % ignore print # Recurse into the code object rdismantle(code) print "END DISMANTLE" return code def rdismantle(co, parent = None): """Dismantles the code object, co. Prints the co_* field values and the co_code disassembly for each code object in the file and recurses into any code objects in the constant pool. """ # Create full name if parent: fullname = parent + "." + co.co_name else: fullname = co.co_name # Print object fields and values print "fullname: \t", fullname print " co_name: \t", co.co_name print " co_filename: \t", co.co_filename print " co_firstlineno:\t", co.co_firstlineno print " co_flags: \t0x%04x" % co.co_flags print " co_stacksize: \t", co.co_stacksize print " co_lnotab[%3d]:\t%s" % \ (len(co.co_lnotab), repr(co.co_lnotab[:8])) print " co_argcount: \t", co.co_argcount print " co_nlocals: \t", co.co_nlocals print " co_freevars: \t", co.co_freevars print " co_cellvars: \t", co.co_cellvars # Print vital compound components tabspacing = "\t\t" print " co_varnames:" i = 0 for item in co.co_varnames: print tabspacing, i, ":\t", item i += 1 print " co_names: " i = 0 for item in co.co_names: print tabspacing, i, ":\t", item i += 1 print " co_consts: " i = 0 for item in co.co_consts: if type(item) == types.StringType and \ len(item) > STRINGTOOLONG: print tabspacing, i, ":\t", repr(item[:STRINGTOOLONG]), "..." else: print tabspacing, i, ":\t", repr(item) i += 1 # Print disassembly print " co_code:" dis.dis(co) print "\n" # Dismantle code objects in constant pool for obj in co.co_consts: if type(obj) == types.CodeType: rdismantle(obj, fullname) return def main(): """Dismantles the source file given as an arg. """ if len(sys.argv) == 2: return dismantle_file(sys.argv[1]) else: print __usage__ if __name__ == "__main__": main()