From 44ef97ec6c804ae641738366e077c44aa38cf109 Mon Sep 17 00:00:00 2001 From: Catena cyber <35799796+catenacyber@users.noreply.github.com> Date: Tue, 8 May 2018 20:15:03 +0200 Subject: [PATCH] Loads shared ELF to analyze a function (#861) * Loads shared ELF to analyze a function See #69 * Bring in recent changes to concolic.py * Loads shared ELF to analyze a function * Fix the 'linux' class method prototype --- manticore/__main__.py | 7 ++++++- manticore/manticore.py | 17 +++++++++++++--- manticore/platforms/linux.py | 39 ++++++++++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/manticore/__main__.py b/manticore/__main__.py index 06cc15a..804ebe6 100644 --- a/manticore/__main__.py +++ b/manticore/__main__.py @@ -36,6 +36,11 @@ def parse_arguments(): help=argparse.SUPPRESS) parser.add_argument('--env', type=str, nargs=1, default=[], action='append', help='Specify symbolic environment variable VARNAME=++++++') + #TODO allow entry as an address + #parser.add_argument('--entry', type=str, default=None, + # help='address as entry point') + parser.add_argument('--entrysymbol', type=str, default=None, + help='symbol as entry point') parser.add_argument('--file', type=str, default=[], action='append', dest='files', help='Specify symbolic input file, \'+\' marks symbolic bytes') parser.add_argument('--names', type=str, default=None, @@ -115,7 +120,7 @@ def main(): env = {key: val for key, val in map(lambda env: env[0].split('='), args.env)} - m = Manticore(args.argv[0], argv=args.argv[1:], env=env, workspace_url=args.workspace, policy=args.policy, disasm=args.disasm, concrete_start=args.data) + m = Manticore(args.argv[0], argv=args.argv[1:], env=env, entry_symbol=args.entrysymbol, workspace_url=args.workspace, policy=args.policy, disasm=args.disasm, concrete_start=args.data) # Fixme(felipe) remove this, move to plugin m.coverage_file = args.coverage diff --git a/manticore/manticore.py b/manticore/manticore.py index c100880..a02721e 100644 --- a/manticore/manticore.py +++ b/manticore/manticore.py @@ -76,7 +76,7 @@ def make_decree(program, concrete_start='', **kwargs): return initial_state -def make_linux(program, argv=None, env=None, symbolic_files=None, concrete_start=''): +def make_linux(program, argv=None, env=None, entry_symbol=None, symbolic_files=None, concrete_start=''): env = {} if env is None else env argv = [] if argv is None else argv env = ['%s=%s' % (k, v) for k, v in env.items()] @@ -86,6 +86,15 @@ def make_linux(program, argv=None, env=None, symbolic_files=None, concrete_start constraints = ConstraintSet() platform = linux.SLinux(program, argv=argv, envp=env, symbolic_files=symbolic_files) + if entry_symbol is not None: + entry_pc = platform._find_symbol(entry_symbol) + if entry_pc is None: + logger.error("No symbol for '%s' in %s", entry_symbol, program) + raise Exception("Symbol not found") + else: + logger.info("Found symbol '%s' (%x)", entry_symbol, entry_pc) + #TODO: use argv as arguments for function + platform.set_entry(entry_pc) initial_state = State(constraints, platform) @@ -249,7 +258,7 @@ class Manticore(Eventful): plugin.manticore = None @classmethod - def linux(cls, path, argv=None, envp=None, symbolic_files=None, concrete_start='', **kwargs): + def linux(cls, path, argv=None, envp=None, entry_symbol=None, symbolic_files=None, concrete_start='', **kwargs): """ Constructor for Linux binary analysis. @@ -258,6 +267,8 @@ class Manticore(Eventful): :type argv: list[str] :param envp: Environment to provide to the binary :type envp: dict[str, str] + :param entry_symbol: Entry symbol to resolve to start execution + :type envp: str :param symbolic_files: Filenames to mark as having symbolic input :type symbolic_files: list[str] :param str concrete_start: Concrete stdin to use before symbolic inputt @@ -266,7 +277,7 @@ class Manticore(Eventful): :rtype: Manticore """ try: - return cls(make_linux(path, argv, envp, symbolic_files, concrete_start), **kwargs) + return cls(make_linux(path, argv, envp, entry_symbol, symbolic_files, concrete_start), **kwargs) except elftools.common.exceptions.ELFError: raise Exception('Invalid binary: {}'.format(path)) diff --git a/manticore/platforms/linux.py b/manticore/platforms/linux.py index 49a1f59..a74fa21 100644 --- a/manticore/platforms/linux.py +++ b/manticore/platforms/linux.py @@ -10,6 +10,8 @@ import socket # Remove in favor of binary.py from elftools.elf.elffile import ELFFile +from elftools.elf.sections import SymbolTableSection +from elftools.elf.descriptions import describe_symbol_type from ..core.cpu.abstractcpu import Interruption, Syscall, ConcretizeArgument from ..core.cpu.cpufactory import CpuFactory @@ -447,6 +449,21 @@ class Linux(Platform): self._function_abi = CpuFactory.get_function_abi(cpu, 'linux', arch) self._syscall_abi = CpuFactory.get_syscall_abi(cpu, 'linux', arch) + def _find_symbol(self, name): + symbol_tables = (s for s in self.elf.iter_sections() + if isinstance(s, SymbolTableSection)) + + for section in symbol_tables: + if section['sh_entsize'] == 0: + continue + + for symbol in section.iter_symbols(): + if describe_symbol_type(symbol['st_info']['type']) == 'FUNC': + if symbol.name == name: + return symbol['st_value'] + + return None + def _execve(self, program, argv, envp): ''' Load `program` and establish program state, such as stack and arguments. @@ -798,6 +815,13 @@ class Linux(Platform): # ARGC cpu.push_int(len(argvlst)) + def set_entry(self, entryPC): + elf_entry = entryPC + if self.elf.header.e_type == 'ET_DYN': + elf_entry += self.load_addr + self.current.PC = elf_entry + logger.debug("Entry point updated: %016x", elf_entry) + def load(self, filename): ''' Loads and an ELF program in memory and prepares the initial CPU state. @@ -826,7 +850,10 @@ class Linux(Platform): continue interpreter_filename = elf_segment.data()[:-1] logger.info('Interpreter filename: %s', interpreter_filename) - interpreter = ELFFile(file(interpreter_filename)) + try: + interpreter = ELFFile(open(interpreter_filename)) + except IOError: + logger.warning('Interpreter not loaded: %s', interpreter_filename) break if interpreter is not None: assert interpreter.get_machine_arch() == elf.get_machine_arch() @@ -848,7 +875,7 @@ class Linux(Platform): end_code = 0 end_data = 0 elf_brk = 0 - load_addr = 0 + self.load_addr = 0 for elf_segment in elf.iter_segments(): if elf_segment.header.p_type != 'PT_LOAD': @@ -879,8 +906,8 @@ class Linux(Platform): logger.debug("Loading elf offset: %08x addr:%08x %08x %s" % (offset, base + vaddr, base + vaddr + memsz, perms)) base = cpu.memory.mmapFile(hint, memsz, perms, elf_segment.stream.name, offset) - vaddr - if load_addr == 0: - load_addr = base + vaddr + if self.load_addr == 0: + self.load_addr = base + vaddr k = base + vaddr + filesz if k > elf_bss: @@ -895,7 +922,7 @@ class Linux(Platform): elf_entry = elf.header.e_entry if elf.header.e_type == 'ET_DYN': - elf_entry += load_addr + elf_entry += self.load_addr entry = elf_entry real_elf_brk = elf_brk @@ -1014,7 +1041,7 @@ class Linux(Platform): at_execfn = cpu.push_bytes(filename + '\x00') self.auxv = { - 'AT_PHDR': load_addr + elf.header.e_phoff, # Program headers for program + 'AT_PHDR': self.load_addr + elf.header.e_phoff, # Program headers for program 'AT_PHENT': elf.header.e_phentsize, # Size of program header entry 'AT_PHNUM': elf.header.e_phnum, # Number of program headers 'AT_PAGESZ': cpu.memory.page_size, # System page size