def decompile(self): self.is_dump = False self.gph, pe_nb_new_syms = self.gctx.dis.get_graph(self.entry) if self.gph is None: error("capstone can't disassemble here") return None self.gph.simplify() if self.gctx.db.loaded and pe_nb_new_syms: self.gctx.db.modified = True try: self.gph.loop_detection(self.entry) ast, correctly_ended = generate_ast(self) if not correctly_ended: debug__("Second try...") self.gph.loop_detection(self.entry, True) ast, _ = generate_ast(self) self.ast = ast except ExcIfelse as e: error("can't have a ifelse here %x" % e.addr) if self.gctx.interactive_mode: return None die() o = self.gctx.libarch.output.Output(self) o._ast(self.entry, ast) self.output = o return o
def load_file(self, filename=None): if filename is None: filename = self.filename if not os.path.exists(filename): error("file {self.filename} doesn't exist".format(self=self)) if self.interactive_mode: return False die() if not os.path.isfile(filename): error("this is not a file".format(self=self)) if self.interactive_mode: return False die() self.db = Database() self.db.load(filename) if self.raw_base != 0: self.db.raw_base = self.raw_base if self.raw_type is not None: self.db.raw_type = self.raw_type if self.raw_big_endian is not None: self.db.raw_is_big_endian = self.raw_big_endian if self.db.loaded: self.raw_base = self.db.raw_base self.raw_type = self.db.raw_type self.raw_big_endian = self.db.raw_is_big_endian try: dis = Disassembler(filename, self.raw_type, self.raw_base, self.raw_big_endian, self.db) except ExcArch as e: error("arch %s is not supported" % e.arch) if self.interactive_mode: return False die() except ExcFileFormat: error("the file is not PE or ELF binary") if self.interactive_mode: return False die() except ExcPEFail as e: error(str(e.e)) error("it seems that there is a random bug in pefile, you shoul retry.") error("please report here https://github.com/joelpx/plasma/issues/16") if self.interactive_mode: return False die() self.dis = dis self.libarch = dis.load_arch_module() return True
def load_file(self, filename=None): if filename is None: filename = self.filename if not os.path.exists(filename): error("file {self.filename} doesn't exist".format(self=self)) if self.interactive_mode: return False die() if not os.path.isfile(filename): error("this is not a file".format(self=self)) if self.interactive_mode: return False die() self.db = Database() self.db.load(self._get_database_path(filename)) if self.raw_base != 0: self.db.raw_base = self.raw_base if self.raw_type is not None: self.db.raw_type = self.raw_type if self.raw_big_endian is not None: self.db.raw_is_big_endian = self.raw_big_endian if self.db.loaded: self.raw_base = self.db.raw_base self.raw_type = self.db.raw_type self.raw_big_endian = self.db.raw_is_big_endian try: dis = Disassembler(filename, self.raw_type, self.raw_base, self.raw_big_endian, self.db) except ExcArch as e: error("arch %s is not supported" % e.arch) if self.interactive_mode: return False die() except ExcFileFormat: error("the file is not PE or ELF binary") if self.interactive_mode: return False die() except ExcPEFail as e: error(str(e.e)) error("it seems that there is a random bug in pefile, you shoul retry.") error("please report here https://github.com/joelpx/plasma/issues/16") if self.interactive_mode: return False die() self.dis = dis self.libarch = dis.load_arch_module() return True
def __register_relocs(self, section): if section.header["sh_offset"] in self.__parsed_reloc_tables: return self.__parsed_reloc_tables.add(section.header["sh_offset"]) relocs = [] for r in section.iter_relocations(): # MIPS64 is just plain old f****d up # https://www.sourceware.org/ml/libc-alpha/2003-03/msg00153.html if self.arch == "MIPS64": # Little endian addionally needs one of its fields reversed... WHY if self.elf.little_endian: r.entry.r_info_sym = r.entry.r_info & 0xFFFFFFFF r.entry.r_info = struct.unpack(">Q", struct.pack("<Q", r.entry.r_info))[0] type_1 = r.entry.r_info & 0xFF type_2 = r.entry.r_info >> 8 & 0xFF type_3 = r.entry.r_info >> 16 & 0xFF extra_sym = r.entry.r_info >> 24 & 0xFF if extra_sym != 0: die("r_info_extra_sym is nonzero??? PLEASE SEND HELP") sym = self.dynsym.get_symbol(r.entry.r_info_sym) if type_1 != 0: r.entry.r_info_type = type_1 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_2 != 0: r.entry.r_info_type = type_2 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_3 != 0: r.entry.r_info_type = type_3 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) else: if "sh_link" in section.header: symtab = self.reader.get_section(section.header["sh_link"]) sym = symtab.get_symbol(r.entry.r_info_sym) else: sym = self.dynsym.get_symbol(r.entry.r_info_sym) reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) return relocs
def console_entry(): gctx = GlobalContext() gctx.parse_args() if gctx.filename is None: die() if not gctx.load_file(): die() if gctx.interactive_mode: from plasma.lib.ui.console import Console gctx.is_interactive = True Console(gctx) else: gctx.api = Api(gctx, None) if gctx.list_sections: for s in gctx.dis.binary.iter_sections(): s.print_header() sys.exit(0) if gctx.syms: gctx.dis.print_symbols() sys.exit(0) ctx = gctx.get_addr_context(gctx.entry) if ctx is None: sys.exit(0) if gctx.do_dump: ctx.dump_asm(gctx.nb_lines).print() sys.exit(0) o = ctx.decompile() if gctx.graph: ctx.gph.dot_graph(gctx.dis.jmptables) if o is not None: if gctx.vim: base = os.path.basename(gctx.filename) + "_" + gctx.entry # re-assign if no colors gctx.libarch.process_ast.assign_colors(ctx, ctx.ast) gctx.color = False generate_vim_syntax(ctx, base + ".vim") sys.stdout = open(base + ".rev", "w+") o.print() if gctx.vim: print("run : vim {0}.rev -S {0}.vim".format(base), file=sys.stderr)
def __register_relocs(self, section): if section.header["sh_offset"] in self.__parsed_reloc_tables: return self.__parsed_reloc_tables.add(section.header["sh_offset"]) relocs = [] for r in section.iter_relocations(): # MIPS64 is just plain old f****d up # https://www.sourceware.org/ml/libc-alpha/2003-03/msg00153.html if self.arch == "MIPS64": # Little endian addionally needs one of its fields reversed... WHY if self.elf.little_endian: r.entry.r_info_sym = r.entry.r_info & 0xFFFFFFFF r.entry.r_info = struct.unpack( ">Q", struct.pack("<Q", r.entry.r_info))[0] type_1 = r.entry.r_info & 0xFF type_2 = r.entry.r_info >> 8 & 0xFF type_3 = r.entry.r_info >> 16 & 0xFF extra_sym = r.entry.r_info >> 24 & 0xFF if extra_sym != 0: die("r_info_extra_sym is nonzero??? PLEASE SEND HELP") sym = self.dynsym.get_symbol(r.entry.r_info_sym) if type_1 != 0: r.entry.r_info_type = type_1 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_2 != 0: r.entry.r_info_type = type_2 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_3 != 0: r.entry.r_info_type = type_3 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) else: if "sh_link" in section.header: symtab = self.reader.get_section(section.header["sh_link"]) sym = symtab.get_symbol(r.entry.r_info_sym) else: sym = self.dynsym.get_symbol(r.entry.r_info_sym) reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) return relocs
def load(self, filename): gc.disable() dirname = os.path.dirname(filename) self.path = dirname + "/" if dirname != "" else "" self.path += "." + os.path.basename(filename) + ".db" if os.path.exists(self.path): info("open database %s" % self.path) fd = open(self.path, "rb") data = fd.read() if data.startswith(b"ZLIB"): data = zlib.decompress(data[4:]) data = msgpack.unpackb(data, encoding="utf-8") fd.close() self.__load_meta(data) self.__load_memory(data) self.__load_symbols(data) self.__load_jmptables(data) self.__load_comments(data) self.__load_functions(data) self.__load_history(data) self.__load_xrefs(data) self.__load_imports(data) self.__load_immediates(data) if self.version <= 1.5: self.__load_labels(data) if self.version < VERSION: die("your version of plasma is too old") elif self.version != VERSION: warning( "the database version is old, some information may be missing" ) self.loaded = True gc.enable()
def load(self, filename): gc.disable() dirname = os.path.dirname(filename) self.path = dirname + "/" if dirname != "" else "" self.path += "." + os.path.basename(filename) + ".db" if os.path.exists(self.path): info("open database %s" % self.path) fd = open(self.path, "rb") data = fd.read() if data.startswith(b"ZLIB"): data = zlib.decompress(data[4:]) data = msgpack.unpackb(data, encoding="utf-8") fd.close() self.__load_meta(data) self.__load_memory(data) self.__load_symbols(data) self.__load_jmptables(data) self.__load_comments(data) self.__load_functions(data) self.__load_history(data) self.__load_xrefs(data) self.__load_imports(data) self.__load_immediates(data) if self.version <= 1.5: self.__load_labels(data) if self.version < VERSION: die("your version of plasma is too old") elif self.version != VERSION: warning("the database version is old, some information may be missing") self.loaded = True gc.enable()
def console_entry(): gctx = GlobalContext() gctx.parse_args() if gctx.color and plasma.lib.colors.VERSION < plasma.lib.colors.CURR_VERSION: info("There is a new version of custom_colors.py. If you did any") info("modifications you can delete it. Otherwise you can copy it") info("somewhere, run again your command then merge the file at hand.") die() if gctx.filename is None: die() if not gctx.load_file(): die() if gctx.interactive_mode: from plasma.lib.ui.console import Console gctx.is_interactive = True Console(gctx) else: gctx.api = Api(gctx, None) if gctx.list_sections: for s in gctx.dis.binary.iter_sections(): s.print_header() sys.exit(0) if gctx.syms: gctx.dis.print_symbols(gctx.sectionsname) sys.exit(0) ctx = gctx.get_addr_context(gctx.entry) if ctx is None: sys.exit(0) if gctx.do_dump: ctx.dump_asm(gctx.nb_lines).print() sys.exit(0) o = ctx.decompile() if gctx.graph: ctx.gph.dot_graph(gctx.dis.jmptables) if o is not None: if gctx.vim: base = os.path.basename(gctx.filename) + "_" + gctx.entry # re-assign if no colors gctx.libarch.process_ast.assign_colors(ctx, ctx.ast) gctx.color = False generate_vim_syntax(ctx, base + ".vim") sys.stdout = open(base + ".rev", "w+") o.print() if gctx.vim: print("run : vim {0}.rev -S {0}.vim".format(base), file=sys.stderr)
def load(self, filename): gc.disable() self.path = filename if os.path.exists(self.path): info("open database %s" % self.path) fd = open(self.path, "rb") data = fd.read() if data.startswith(b"ZLIB"): data = zlib.decompress(data[4:]) data = msgpack.unpackb(data, encoding="utf-8") fd.close() self.__load_meta(data) if self.version == LAST_COMPATIBLE: warning("the database version is old, some information may be missing") elif self.version < LAST_COMPATIBLE: die("the database is too old") self.__load_memory(data) self.__load_symbols(data) self.__load_jmptables(data) self.__load_comments(data) self.__load_functions(data) self.__load_history(data) self.__load_xrefs(data) self.__load_imports(data) self.__load_immediates(data) self.__load_inverted_cond(data) self.loaded = True gc.enable()
def init_address(self, entry, quiet=False): if isinstance(entry, int): self.entry = entry return True if entry == "EP": self.entry = self.gctx.dis.binary.get_entry_point() return True if entry is None: if self.gctx.raw_type is not None: self.entry = 0 return True self.entry = self.gctx.db.symbols.get("main", None) or \ self.gctx.db.symbols.get("_main", None) or \ self.gctx.dis.binary.get_entry_point() if self.entry is None: error("symbol main or _main not found, try with EP") if self.gctx.interactive_mode: return False die() return True is_hexa = entry.startswith("0x") if not is_hexa and self.gctx.api.is_reserved_prefix(entry): entry = entry[entry.index("_") + 1:] is_hexa = True if is_hexa: try: self.entry = int(entry, 16) except: if not quiet: error("bad hexa string %s" % entry) if self.gctx.interactive_mode: return False die() return True self.entry = self.gctx.db.demangled.get(entry, None) or \ self.gctx.db.symbols.get(entry, None) or \ self.gctx.dis.binary.section_names.get(entry, None) if self.entry is None: if not quiet: error("symbol %s not found" % entry) if self.gctx.interactive_mode: return False die() return True
def parse_args(self): parser = ArgumentParser(description= 'Reverse engineering for x86/ARM/MIPS binaries. Generation of pseudo-C. ' 'Supported formats : ELF, PE. More commands available in the interactive' ' mode. https://github.com/joelpx/plasma') parser.add_argument('filename', nargs='?', metavar='FILENAME') parser.add_argument('-nc', '--nocolor', action='store_true') parser.add_argument('-g', '--graph', action='store_true', help='Generate a file graph.dot.') parser.add_argument('--noandif', action='store_true', help="Print normal 'if' instead of 'andif'") parser.add_argument('--datasize', type=int, default=30, metavar='N', help='default 30, maximum of chars to display for strings or bytes array.') parser.add_argument('-x', '--entry', metavar='SYMBOLNAME|0xXXXXX|EP', help='Pseudo-decompilation, default is main. EP stands for entry point.') parser.add_argument('--vim', action='store_true', help='Generate syntax colors for vim') parser.add_argument('-s', '--symbols', action='store_true', help='Print all symbols') parser.add_argument('--sections', action='store_true', help='Print all sections') parser.add_argument('--dump', action='store_true', help='Dump asm without decompilation') parser.add_argument('-l', '--lines', type=int, default=30, metavar='N', help='Max lines used with --dump') parser.add_argument('--nbytes', type=int, default=0, metavar='N', help='Print n bytes.') parser.add_argument('-i', '--interactive', action='store_true', help='Interactive mode') parser.add_argument('-d', '--opt_debug', action='store_true') parser.add_argument('--raw', metavar='x86|x64|arm|mips|mips64', help='Consider the input file as a raw binary') parser.add_argument('--rawbase', metavar='0xXXXXX', help='Set base address of a raw file (default=0)') parser.add_argument('--rawbe', action='store_true', help='If not set it\'s in little endian') parser.add_argument('-na', '--noautoanalyzer', action='store_true', help='Disable analysis on the entry point / symbols and don\'t scan memmory. You can force it with the command push_analyze_symbols.') parser.add_argument('--debugsp', action='store_true', help="Print the stack offset on each instructions. Warning: these values will not be saved in the database.") args = parser.parse_args() self.debug = args.opt_debug self.print_andif = not args.noandif self.color = not args.nocolor self.max_data_size = args.datasize self.filename = args.filename self.raw_type = args.raw self.raw_base = args.rawbase self.syms = args.symbols self.entry = args.entry self.do_dump = args.dump self.vim = args.vim self.interactive_mode = args.interactive self.nb_lines = args.lines self.graph = args.graph self.raw_big_endian = args.rawbe self.list_sections = args.sections self.autoanalyzer = not args.noautoanalyzer self.debugsp = args.debugsp if args.nbytes == 0: self.nbytes = 4 self.print_bytes = False else: self.nbytes = int(args.nbytes) self.print_bytes = True if self.raw_base is not None: try: self.raw_base = int(self.raw_base, 16) except: error("--rawbase must be in hex format") die() else: self.raw_base = 0
def parse_args(self): parser = ArgumentParser( description="Reverse engineering for x86/ARM/MIPS binaries. Generation of pseudo-C. " "Supported formats : ELF, PE. More commands available in the interactive" " mode. https://github.com/joelpx/plasma" ) parser.add_argument("filename", nargs="?", metavar="FILENAME") parser.add_argument("-nc", "--nocolor", action="store_true") parser.add_argument("-g", "--graph", action="store_true", help="Generate a file graph.dot.") parser.add_argument("--noandif", action="store_true", help="Print normal 'if' instead of 'andif'") parser.add_argument( "--datasize", type=int, default=30, metavar="N", help="default 30, maximum of chars to display for strings or bytes array.", ) parser.add_argument( "-x", "--entry", metavar="SYMBOLNAME|0xXXXXX|EP", help="Pseudo-decompilation, default is main. EP stands for entry point.", ) parser.add_argument("--vim", action="store_true", help="Generate syntax colors for vim") parser.add_argument("-s", "--symbols", action="store_true", help="Print all symbols") parser.add_argument("--sections", action="store_true", help="Print all sections") parser.add_argument("--dump", action="store_true", help="Dump asm without decompilation") parser.add_argument("-l", "--lines", type=int, default=30, metavar="N", help="Max lines used with --dump") parser.add_argument("--nbytes", type=int, default=0, metavar="N", help="Print n bytes.") parser.add_argument("-i", "--interactive", action="store_true", help="Interactive mode") parser.add_argument("-d", "--opt_debug", action="store_true") parser.add_argument("-ns", "--nosectionsname", action="store_true") parser.add_argument("--raw", metavar="x86|x64|arm|mips|mips64", help="Consider the input file as a raw binary") parser.add_argument("--rawbase", metavar="0xXXXXX", help="Set base address of a raw file (default=0)") parser.add_argument("--rawbe", action="store_true", help="If not set it's in little endian") parser.add_argument( "-na", "--noautoanalyzer", action="store_true", help="Disable analysis on the entry point / symbols and don't scan memmory. You can force it with the command push_analyze_symbols.", ) args = parser.parse_args() self.debug = args.opt_debug self.print_andif = not args.noandif self.color = not args.nocolor self.sectionsname = not args.nosectionsname self.max_data_size = args.datasize self.filename = args.filename self.raw_type = args.raw self.raw_base = args.rawbase self.syms = args.symbols self.entry = args.entry self.do_dump = args.dump self.vim = args.vim self.interactive_mode = args.interactive self.nb_lines = args.lines self.graph = args.graph self.raw_big_endian = args.rawbe self.list_sections = args.sections self.autoanalyzer = not args.noautoanalyzer if args.nbytes == 0: self.nbytes = 4 self.print_bytes = False else: self.nbytes = int(args.nbytes) self.print_bytes = True if self.raw_base is not None: try: self.raw_base = int(self.raw_base, 16) except: error("--rawbase must be in hex format") die() else: self.raw_base = 0
def parse_args(self): parser = ArgumentParser( description= 'Reverse engineering for x86/ARM/MIPS binaries. Generation of pseudo-C. ' 'Supported formats : ELF, PE. More commands available in the interactive' ' mode. https://github.com/joelpx/plasma') parser.add_argument('filename', nargs='?', metavar='FILENAME') parser.add_argument('-nc', '--nocolor', action='store_true') parser.add_argument('-g', '--graph', action='store_true', help='Generate a file graph.dot.') parser.add_argument('--noandif', action='store_true', help="Print normal 'if' instead of 'andif'") parser.add_argument( '--datasize', type=int, default=30, metavar='N', help= 'default 30, maximum of chars to display for strings or bytes array.' ) parser.add_argument( '-x', '--entry', metavar='SYMBOLNAME|0xXXXXX|EP', help= 'Pseudo-decompilation, default is main. EP stands for entry point.' ) parser.add_argument('--vim', action='store_true', help='Generate syntax colors for vim') parser.add_argument('-s', '--symbols', action='store_true', help='Print all symbols') parser.add_argument('--sections', action='store_true', help='Print all sections') parser.add_argument('--dump', action='store_true', help='Dump asm without decompilation') parser.add_argument('-l', '--lines', type=int, default=30, metavar='N', help='Max lines used with --dump') parser.add_argument('--nbytes', type=int, default=0, metavar='N', help='Print n bytes.') parser.add_argument('-i', '--interactive', action='store_true', help='Interactive mode') parser.add_argument('-d', '--opt_debug', action='store_true') parser.add_argument('--raw', metavar='x86|x64|arm|mips|mips64', help='Consider the input file as a raw binary') parser.add_argument('--rawbase', metavar='0xXXXXX', help='Set base address of a raw file (default=0)') parser.add_argument('--rawbe', action='store_true', help='If not set it\'s in little endian') parser.add_argument( '-na', '--noautoanalyzer', action='store_true', help= 'Disable analysis on the entry point / symbols and don\'t scan memmory. You can force it with the command push_analyze_symbols.' ) parser.add_argument( '--debugsp', action='store_true', help= "Print the stack offset on each instructions. Warning: these values will not be saved in the database." ) args = parser.parse_args() self.debug = args.opt_debug self.print_andif = not args.noandif self.color = not args.nocolor self.max_data_size = args.datasize self.filename = args.filename self.raw_type = args.raw self.raw_base = args.rawbase self.syms = args.symbols self.entry = args.entry self.do_dump = args.dump self.vim = args.vim self.interactive_mode = args.interactive self.nb_lines = args.lines self.graph = args.graph self.raw_big_endian = args.rawbe self.list_sections = args.sections self.autoanalyzer = not args.noautoanalyzer self.debugsp = args.debugsp if args.nbytes == 0: self.nbytes = 4 self.print_bytes = False else: self.nbytes = int(args.nbytes) self.print_bytes = True if self.raw_base is not None: try: self.raw_base = int(self.raw_base, 16) except: error("--rawbase must be in hex format") die() else: self.raw_base = 0
def console_entry(): gctx = GlobalContext() gctx.parse_args() if gctx.color and plasma.lib.colors.VERSION < plasma.lib.colors.CURR_VERSION: info("There is a new version of custom_colors.py. If you did any") info("modifications you can delete it. Otherwise you can copy it") info("somewhere, run again your command then merge the file at hand.") die() if gctx.filename is None: die() if not gctx.load_file(): die() if gctx.interactive_mode: from plasma.lib.ui.console import Console gctx.is_interactive = True Console(gctx) else: gctx.api = Api(gctx, None) if gctx.list_sections: for s in gctx.dis.binary.iter_sections(): s.print_header() sys.exit(0) if gctx.syms: gctx.dis.print_symbols() sys.exit(0) ctx = gctx.get_addr_context(gctx.entry) if ctx is None: sys.exit(0) if gctx.do_dump: ctx.dump_asm(gctx.nb_lines).print() sys.exit(0) o = ctx.decompile() if gctx.graph: ctx.gph.dot_graph(gctx.dis.jmptables) if o is not None: if gctx.vim: base = os.path.basename(gctx.filename) + "_" + gctx.entry # re-assign if no colors gctx.libarch.process_ast.assign_colors(ctx, ctx.ast) gctx.color = False generate_vim_syntax(ctx, base + ".vim") sys.stdout = open(base + ".rev", "w+") o.print() if gctx.vim: print("run : vim {0}.rev -S {0}.vim".format(base), file=sys.stderr)