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
This commit is contained in:
Catena cyber 2018-05-08 20:15:03 +02:00 committed by Yan Ivnitskiy
parent b9a515ccfe
commit 44ef97ec6c
3 changed files with 53 additions and 10 deletions

View File

@ -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

View File

@ -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))

View File

@ -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