| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- # Copyright (c) 2015-2016, 2818-2020, 2024 by Rocky Bernstein
- # Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
- # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
- # Copyright (c) 1999 John Aycock
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program 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. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- CPython magic- and version- independent disassembly routines
- There are two reasons we can't use Python's built-in routines
- from dis. First, the bytecode we are extracting may be from a different
- version of Python (different magic number) than the version of Python
- that is doing the extraction.
- Second, we need structured instruction information for the
- (de)-parsing step. Python 3.4 and up provides this, but we still do
- want to run on earlier Python versions.
- """
- import sys
- from collections import deque
- from xdis import check_object_path, iscode, load_module
- from decompyle3.scanner import get_scanner
- def disco(version: str, co, out=None, is_pypy=False) -> None:
- """
- diassembles and deparses a given code block 'co'
- """
- assert iscode(co)
- # store final output stream for case of error
- real_out = out or sys.stdout
- print(f"# Python {version}", file=real_out)
- if co.co_filename:
- print(f"# Embedded file name: {co.co_filename}", file=real_out)
- scanner = get_scanner(version, is_pypy=is_pypy)
- queue = deque([co])
- disco_loop(scanner.ingest, queue, real_out)
- def disco_loop(disasm, queue, real_out):
- while len(queue) > 0:
- co = queue.popleft()
- if co.co_name != "<module>":
- print(
- "\n# %s line %d of %s"
- % (co.co_name, co.co_firstlineno, co.co_filename),
- file=real_out,
- )
- tokens, customize = disasm(co)
- for t in tokens:
- if iscode(t.pattr):
- queue.append(t.pattr)
- elif iscode(t.attr):
- queue.append(t.attr)
- print(t, file=real_out)
- pass
- pass
- # def disassemble_fp(fp, outstream=None):
- # """
- # disassemble Python byte-code from an open file
- # """
- # (version, timestamp, magic_int, co, is_pypy,
- # source_size) = load_from_fp(fp)
- # if type(co) == list:
- # for con in co:
- # disco(version, con, outstream)
- # else:
- # disco(version, co, outstream, is_pypy=is_pypy)
- # co = None
- def disassemble_file(filename: str, outstream=None) -> None:
- """
- disassemble Python byte-code file (.pyc)
- If given a Python source file (".py") file, we'll
- try to find the corresponding compiled object.
- """
- filename = check_object_path(filename)
- (version, timestamp, magic_int, co, is_pypy, source_size, sip_hash) = load_module(
- filename
- )
- if isinstance(co, list):
- for con in co:
- disco(version, con, outstream)
- else:
- disco(version, co, outstream, is_pypy=is_pypy)
- co = None
- def _test() -> None:
- """Simple test program to disassemble a file."""
- argc = len(sys.argv)
- if argc != 2:
- if argc == 1:
- fn = __file__
- else:
- sys.stderr.write(f"usage: {__file__} [-|CPython compiled file]\n")
- sys.exit(2)
- else:
- fn = sys.argv[1]
- disassemble_file(fn)
- if __name__ == "__main__":
- _test()
|