Files
cea-sec-miasm/miasm2/analysis/binary.py
T
2017-03-25 20:34:53 +01:00

234 lines
7.4 KiB
Python

import logging
from miasm2.core.bin_stream import bin_stream_str, bin_stream_elf, bin_stream_pe
from miasm2.jitter.csts import PAGE_READ
from miasm2.core.asmblock import AsmSymbolPool
log = logging.getLogger("binary")
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("%(levelname)-5s: %(message)s"))
log.addHandler(console_handler)
log.setLevel(logging.ERROR)
# Container
## Exceptions
class ContainerSignatureException(Exception):
"The container does not match the current container signature"
class ContainerParsingException(Exception):
"Error during container parsing"
## Parent class
class Container(object):
"""Container abstraction layer
This class aims to offer a common interface for abstracting container
such as PE or ELF.
"""
available_container = [] # Available container formats
fallback_container = None # Fallback container format
@classmethod
def from_string(cls, data, vm=None, addr=None):
"""Instanciate a container and parse the binary
@data: str containing the binary
@vm: (optional) VmMngr instance to link with the executable
@addr: (optional) Base address for the binary. If set,
force the unknown format
"""
log.info('Load binary')
if not addr:
addr = 0
else:
# Force fallback mode
log.warning('Fallback to string input (offset=%s)', hex(addr))
return cls.fallback_container(data, vm, addr)
# Try each available format
for container_type in cls.available_container:
try:
return container_type(data, vm)
except ContainerSignatureException:
continue
except ContainerParsingException, error:
log.error(error)
# Fallback mode
log.warning('Fallback to string input (offset=%s)', hex(addr))
return cls.fallback_container(data, vm, addr)
@classmethod
def register_container(cls, container):
"Add a Container format"
cls.available_container.append(container)
@classmethod
def register_fallback(cls, container):
"Set the Container fallback format"
cls.fallback_container = container
@classmethod
def from_stream(cls, stream, *args, **kwargs):
"""Instanciate a container and parse the binary
@stream: stream to use as binary
@vm: (optional) VmMngr instance to link with the executable
@addr: (optional) Shift to apply before parsing the binary. If set,
force the unknown format
"""
return Container.from_string(stream.read(), *args, **kwargs)
def parse(self, data, *args, **kwargs):
"Launch parsing of @data"
raise NotImplementedError("Abstract method")
def __init__(self, *args, **kwargs):
"Alias for 'parse'"
# Init attributes
self._executable = None
self._bin_stream = None
self._entry_point = None
self._arch = None
self._symbol_pool = AsmSymbolPool()
# Launch parsing
self.parse(*args, **kwargs)
@property
def bin_stream(self):
"Return the BinStream instance corresponding to container content"
return self._bin_stream
@property
def executable(self):
"Return the abstract instance standing for parsed executable"
return self._executable
@property
def entry_point(self):
"Return the detected entry_point"
return self._entry_point
@property
def arch(self):
"Return the guessed architecture"
return self._arch
@property
def symbol_pool(self):
"AsmSymbolPool instance preloaded with container symbols (if any)"
return self._symbol_pool
## Format dependent classes
class ContainerPE(Container):
"Container abstraction for PE"
def parse(self, data, vm=None):
from miasm2.jitter.loader.pe import vm_load_pe, guess_arch
from elfesteem import pe_init
# Parse signature
if not data.startswith('MZ'):
raise ContainerSignatureException()
# Build executable instance
try:
if vm is not None:
self._executable = vm_load_pe(vm, data)
else:
self._executable = pe_init.PE(data)
except Exception, error:
raise ContainerParsingException('Cannot read PE: %s' % error)
# Check instance validity
if not self._executable.isPE() or \
self._executable.NTsig.signature_value != 0x4550:
raise ContainerSignatureException()
# Guess the architecture
self._arch = guess_arch(self._executable)
# Build the bin_stream instance and set the entry point
try:
self._bin_stream = bin_stream_pe(self._executable.virt)
ep_detected = self._executable.Opthdr.AddressOfEntryPoint
self._entry_point = self._executable.rva2virt(ep_detected)
except Exception, error:
raise ContainerParsingException('Cannot read PE: %s' % error)
class ContainerELF(Container):
"Container abstraction for ELF"
def parse(self, data, vm=None):
from miasm2.jitter.loader.elf import vm_load_elf, guess_arch
from elfesteem import elf_init
# Parse signature
if not data.startswith('\x7fELF'):
raise ContainerSignatureException()
# Build executable instance
try:
if vm is not None:
self._executable = vm_load_elf(vm, data)
else:
self._executable = elf_init.ELF(data)
except Exception, error:
raise ContainerParsingException('Cannot read ELF: %s' % error)
# Guess the architecture
self._arch = guess_arch(self._executable)
# Build the bin_stream instance and set the entry point
try:
self._bin_stream = bin_stream_elf(self._executable.virt)
self._entry_point = self._executable.Ehdr.entry
except Exception, error:
raise ContainerParsingException('Cannot read ELF: %s' % error)
# Add known symbols
for symb_source_name in [".symtab", ".dynsym"]:
symb_source = self._executable.getsectionbyname(symb_source_name)
if symb_source is None:
continue
for name, symb in symb_source.symbols.iteritems():
offset = symb.value
if offset == 0:
continue
try:
self._symbol_pool.add_label(name, offset)
except ValueError:
# Two symbols points on the same offset
log.warning("Same offset (%s) for %s and %s",
(hex(offset),
name,
self._symbol_pool.getby_offset(offset)))
continue
class ContainerUnknown(Container):
"Container abstraction for unknown format"
def parse(self, data, vm, addr):
self._bin_stream = bin_stream_str(data, shift=addr)
if vm is not None:
vm.add_memory_page(addr,
PAGE_READ,
data)
self._executable = None
self._entry_point = 0
## Register containers
Container.register_container(ContainerPE)
Container.register_container(ContainerELF)
Container.register_fallback(ContainerUnknown)