Ejemplo n.º 1
0
 def __init__(self, bv):
     BackgroundTaskThread.__init__(self, "", True)
     self.progress = 'genesis: Fixing up ROM checksum...'
     self.rom_start = 0x200
     self.checksum_off = 0x18e
     self.bv = bv
     self.br = BinaryReader(self.bv)
    def __init__(self, view):
        super(VMVisitor, self).__init__()

        self.view = view
        self.bw = BinaryWriter(view)
        self.br = BinaryReader(view)

        self.regs = {r: 0 for r in Architecture['VMArch'].regs}
Ejemplo n.º 3
0
class GenesisCallTableEnum(BackgroundTaskThread):
    def __init__(self, bv):
        BackgroundTaskThread.__init__(self, "", True)
        self.progress = 'genesis: Enumerating call tables...'
        self.bv = bv
        self.br = BinaryReader(self.bv)

    def find_call_tables(self):
        base_addrs = []
        for func in self.bv:
            if not func.medium_level_il.ssa_form:
                continue

            for block in func.medium_level_il.ssa_form:
                for instr in block:
                    branch_operations = [
                        MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA,
                        MediumLevelILOperation.MLIL_JUMP,
                        MediumLevelILOperation.MLIL_GOTO
                    ]

                    if instr.operation not in branch_operations:
                        continue

                    if type(instr.dest) == int:
                        continue

                    if instr.dest.operation == MediumLevelILOperation.MLIL_ADD:
                        if instr.dest.operands[
                                0].operation == MediumLevelILOperation.MLIL_CONST:
                            base_addrs.append(instr.dest.operands[0].constant)
        return base_addrs

    def disas_call_tables(self, base_addrs):
        count = 0
        for addr in base_addrs:
            i = addr
            while True:
                self.br.seek(i)
                opcode = self.br.read32be()
                if (opcode >> 24) == 0x60:
                    if not self.bv.get_function_at(i):
                        self.bv.add_function(i)
                        count += 1
                else:
                    break
                i += 4

        return count

    def run(self):
        self.bv.platform = Platform['M68000']
        call_table_addrs = self.find_call_tables()
        if not call_table_addrs:
            return
        count = self.disas_call_tables(call_table_addrs)
        show_message_box(
            'genesis', 'Disassembled {} call table instructions'.format(count))
Ejemplo n.º 4
0
  def __init__(self, bnc):
    self.bnc = bnc

    self.br = BinaryReader(bnc.bv)

    self.hexScreen = curses.newpad(curses.LINES-1, curses.COLS)
    self.hexOffset = 0
    self.hexCursor = bnc.pos
    self.loadHexLines()
class VMVisitor(BNILVisitor):
    def __init__(self, view):
        super(VMVisitor, self).__init__()

        self.view = view
        self.bw = BinaryWriter(view)
        self.br = BinaryReader(view)

        self.regs = {r: 0 for r in Architecture['VMArch'].regs}

    def visit_LLIL_STORE(self, expr):
        dest = self.visit(expr.dest)
        src = self.visit(expr.src)

        if None not in (dest, src):
            self.bw.seek(dest)
            self.bw.write8(src)

    def visit_LLIL_CONST(self, expr):
        return expr.constant

    def visit_LLIL_CONST_PTR(self, expr):
        return expr.constant

    def visit_LLIL_SET_REG(self, expr):
        dest = expr.dest.name
        src = self.visit(expr.src)

        if src is not None:
            self.regs[dest] = src

    def visit_LLIL_LOAD(self, expr):
        src = self.visit(expr.src)

        if src is not None:
            self.br.seek(src)
            return self.br.read8()

    def visit_LLIL_XOR(self, expr):
        left = self.visit(expr.left)
        right = self.visit(expr.right)

        if None not in (left, right):
            return left ^ right

    def visit_LLIL_REG(self, expr):
        src = expr.src

        return self.regs[src.name]

    def visit_LLIL_NORET(self, expr):
        log_alert("VM Halted.")
Ejemplo n.º 6
0
    def _add_interrupt_symbols(self, data):
        reader = BinaryReader(data)

        # Skip stack pointer
        reader.seek(4)

        for interrupt in INTERRUPT_TABLE:
            address = reader.read32() & ~1

            if address != 0:
                symbol = Symbol(SymbolType.FunctionSymbol, address, interrupt)
                self.define_auto_symbol(symbol)
                self.add_function(address)
Ejemplo n.º 7
0
    def is_valid_for_data(self, data):
        minimum_header_size = len(INTERRUPT_TABLE) * 4 + 4

        if len(data) < minimum_header_size:
            return False

        reader = BinaryReader(data)
        stack_pointer = reader.read32()
        if stack_pointer < 0x20000000 or stack_pointer >= 0x20004000:
            return False

        reset = reader.read32()
        if reset > 0xFFFF:
            return False

        return True
Ejemplo n.º 8
0
    def __init__(self, bv, filepath=None, directories=None):
        self.progress_banner = "Running YARA scan"
        BackgroundTaskThread.__init__(self, self.progress_banner, True)

        self.bv = bv
        self.reader = BinaryReader(self.bv)
        self.rules = []
        self.results = []

        # Ensure that the tag types exist before using it
        if "YARA Matches" not in bv.tag_types:
            bv.create_tag_type("YARA Matches", "🔎")

        if filepath:
            self.load_signature(filepath)

        if directories:
            self.load_signatures(directories)
Ejemplo n.º 9
0
    def parse_format(self, data):
        """
        This is a helper function to parse our BS format
        :param data:
        :return:
        """
        reader = BinaryReader(data)
        reader.seek(4)
        loading_addr = reader.read32()
        flags = SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable
        self.add_auto_segment(loading_addr,
                              len(data) - 8, 8,
                              len(data) - 8, flags)
        self.add_auto_section("text", loading_addr,
                              len(data) - 8,
                              SectionSemantics.ReadOnlyCodeSectionSemantics)

        self.add_entry_point(loading_addr)
Ejemplo n.º 10
0
 def evaluate_llil(self, state, llil):
     if llil.operation == LowLevelILOperation.LLIL_CONST:
         return llil.operands[0]
     elif llil.operation == LowLevelILOperation.LLIL_CONST_PTR:
         return llil.operands[0]
     elif llil.operation == LowLevelILOperation.LLIL_REG:
         reg = llil.operands[0].name
         return state.registers[reg]
     elif llil.operation == LowLevelILOperation.LLIL_LOAD:
         addr = self.evaluate_llil(state, llil.operands[0])
         reader = BinaryReader(state.memory_view)
         reader.seek(addr)
         # TODO: 32-bit
         deref = reader.read64()
         return deref
     elif llil.operation == LowLevelILOperation.LLIL_ADD:
         return sum(self.evaluate_llil(state, op) for op in llil.operands)
     else:
         raise NotImplementedError('todo: evaluate llil for %s' %
                                   llil.operation)
Ejemplo n.º 11
0
    def _parse_format(self, data):
        self.load_address = self.get_address_input("Base Address",
                                                   "Base Address")
        reader = BinaryReader(data)
        reader.seek(4)
        entry_point = reader.read32() & ~1
        self.add_entry_point(entry_point)

        # SRAM
        self.add_auto_segment(
            0x20000000, 0x4000, 0, 0, SegmentFlag.SegmentReadable
            | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable)

        # Flash
        self.add_auto_segment(
            self.load_address, len(data), 0, len(data),
            SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable)

        self._add_hardware_registers()
        self._add_interrupt_symbols(data)
        return True
Ejemplo n.º 12
0
    def __init__(self,
                 bv: BinaryView,
                 delphi_version: int,
                 offset_ptr_size=-1,
                 start=-1,
                 end=-1):
        self._offset_ptr_size = offset_ptr_size if offset_ptr_size > 0 else bv.address_size
        self._start = start if start >= 0 else bv.start
        self._end = end if end > 0 else bv.end
        self._vmt_list: List[DelphiVMT] = []
        self._bv = bv
        self._br = BinaryReader(bv)
        self._delphi_version = delphi_version
        self._vmt_offsets = VMTOffsets(delphi_version, self._offset_ptr_size)

        # Not really sure about this but it's working on my test binary (Win64 Delphi v10.3)
        # Need to check offsets for macOS
        # I'll clean that later
        if self._bv.view_type == 'PE' and self._offset_ptr_size == 8:
            for x, y in self._vmt_offsets.__dict__.items():
                setattr(self._vmt_offsets, x, y + -24)
Ejemplo n.º 13
0
def goto_var(il: MediumLevelILInstruction):
    function = il.function.source_function
    view = function.view

    jump_value = JumpVisitor().visit(il)

    if jump_value is None:
        print("Jump target not constant")
        function.reanalyze()
        return

    br = BinaryReader(view)
    br.seek(jump_value)
    if view.arch.address_size == 4:
        target = br.read32le()
    elif view.arch.address_size == 8:
        target = br.read64le()
    else:
        # wtf????
        function.reanalyze()
        return

    function.set_user_indirect_branches(il.address, [(view.arch, target)])

    return target
Ejemplo n.º 14
0
    def find(self, bv, start, end):
        """

        :type bv: BinaryView
        :type start: int
        :type end: int
        """
        br = BinaryReader(bv, Endianness.LittleEndian)
        br.offset = start
        ends = []
        offset = bv.start
        while br.offset < end:
            start_address = br.read32()
            end_address = br.read32()
            unwind_information = br.read32()
            if start_address == 0 and end_address == 0 and unwind_information == 0:
                break
            start_address += offset
            end_address += offset
            ends.append(end_address)
            current = bv.get_function_at(start_address)  # type: Function
            if current is None or current.start != start_address:
                # if not bv.get_basic_blocks_at(start_address):
                bv.create_user_function(start_address)
                f = bv.get_function_at(start_address)  # type: Function
                if f.name[0:4] == 'sub_':
                    f.name += '_pdata'
                self.found += 1
        bv.update_analysis_and_wait()

        for end_address in ends:
            if not bv.get_functions_containing(end_address - 1):
                print "Expected pdata end_address to be in function " + hex(
                    end_address)
Ejemplo n.º 15
0
    def evaluate_llil(self, state, llil):
        # Interpreter for LLIL instructions, using data from state
        if llil.operation == LowLevelILOperation.LLIL_CONST:
            return llil.operands[0]
        elif llil.operation == LowLevelILOperation.LLIL_CONST_PTR:
            return llil.operands[0]
        elif llil.operation == LowLevelILOperation.LLIL_REG:
            reg = llil.operands[0].name
            return state.registers[reg]
        elif llil.operation == LowLevelILOperation.LLIL_LOAD:
            addr = self.evaluate_llil(state, llil.operands[0])
            # Have to read from addr llil.size bytes
            reader = BinaryReader(state.memory_view)
            reader.seek(addr)

            if llil.size == 1:
                deref = reader.read8()
            elif llil.size == 2:
                deref = reader.read16()
            elif llil.size == 4:
                deref = reader.read32()
            else:
                deref = reader.read64()
            # Unimplemented: 128-bit, etc

            return deref
        elif llil.operation == LowLevelILOperation.LLIL_ADD:
            return self.evaluate_llil(state,
                                      llil.operands[0]) + self.evaluate_llil(
                                          state, llil.operands[1])
        elif llil.operation == LowLevelILOperation.LLIL_SUB:
            return self.evaluate_llil(state,
                                      llil.operands[0]) - self.evaluate_llil(
                                          state, llil.operands[1])
        elif llil.operation == LowLevelILOperation.LLIL_MUL:
            return self.evaluate_llil(state,
                                      llil.operands[0]) * self.evaluate_llil(
                                          state, llil.operands[1])
        elif llil.operation == LowLevelILOperation.LLIL_LSL:
            return self.evaluate_llil(state,
                                      llil.operands[0]) << self.evaluate_llil(
                                          state, llil.operands[1])
        elif llil.operation == LowLevelILOperation.LLIL_LSR:
            return self.evaluate_llil(state,
                                      llil.operands[0]) >> self.evaluate_llil(
                                          state, llil.operands[1])
        else:
            raise NotImplementedError('todo: evaluate llil for %s' %
                                      llil.operation)
Ejemplo n.º 16
0
    def __init__(self,
                 bv: BinaryView,
                 delphi_version: int,
                 address: int,
                 analyzer: DelphiAnalyzer,
                 offset_ptr_size=-1):
        self._offset_ptr_size = offset_ptr_size if offset_ptr_size > 0 else bv.address_size
        self._vmt_address = address
        self._analyzer = analyzer
        self._is_valid = False
        self._bv = bv
        self._br = BinaryReader(bv)
        self._vmt_offsets = analyzer.vmt_offsets
        self._class_name = ''
        self._instance_size = 0
        self._parent_vmt = 0
        self._table_list: Mapping[int, str] = {}
        self._virtual_methods: Mapping[int, str] = {}

        if not self._check_self_ptr():
            return

        if not self._resolve_name():
            return

        if not self._resolve_instance_size():
            return

        if not self._resolve_parent_vmt():
            return

        if not self._resolve_table_list():
            return

        if not self._resolve_virtual_methods():
            return

        self._is_valid = True
Ejemplo n.º 17
0
class GenesisChecksum(BackgroundTaskThread):
    def __init__(self, bv):
        BackgroundTaskThread.__init__(self, "", True)
        self.progress = 'genesis: Fixing up ROM checksum...'
        self.rom_start = 0x200
        self.checksum_off = 0x18e
        self.bv = bv
        self.br = BinaryReader(self.bv)

    def _calculate_checksum(self):
        self.br.seek(self.rom_start)
        checksum = self.br.read16be()
        while True:
            checksum = (checksum + self.br.read16be()) & 0x0000ffff
            if self.br.eof:
                break

        return checksum

    def run(self):
        checksum = self._calculate_checksum()
        self.bv.write(self.checksum_off, struct.pack('>H', checksum))
        show_message_box('genesis', 'ROM checksum has been updated')
Ejemplo n.º 18
0
class DelphiVMT(object):
    '''
    TODO: Doc
    '''
    def __init__(self,
                 bv: BinaryView,
                 delphi_version: int,
                 address: int,
                 analyzer: DelphiAnalyzer,
                 offset_ptr_size=-1):
        self._offset_ptr_size = offset_ptr_size if offset_ptr_size > 0 else bv.address_size
        self._vmt_address = address
        self._analyzer = analyzer
        self._is_valid = False
        self._bv = bv
        self._br = BinaryReader(bv)
        self._vmt_offsets = analyzer.vmt_offsets
        self._class_name = ''
        self._instance_size = 0
        self._parent_vmt = 0
        self._table_list: Mapping[int, str] = {}
        self._virtual_methods: Mapping[int, str] = {}

        if not self._check_self_ptr():
            return

        if not self._resolve_name():
            return

        if not self._resolve_instance_size():
            return

        if not self._resolve_parent_vmt():
            return

        if not self._resolve_table_list():
            return

        if not self._resolve_virtual_methods():
            return

        self._is_valid = True

    def __repr__(self):
        return str(self)

    def __str__(self):
        if not self._is_valid:
            return f'<InvalidVmt address=0x{self._vmt_address:08X}>'

        return (f'<{self._class_name} start=0x{self.start:08X} '
                f'instance_size=0x{self._instance_size:X}>')

    ## Properties

    @property
    def vmt_address(self) -> int:
        return self._vmt_address

    @property
    def is_valid(self) -> bool:
        return self._is_valid

    @property
    def class_name(self) -> str:
        return self._class_name

    @property
    def instance_size(self) -> int:
        return self._instance_size

    @property
    def parent_vmt(self) -> int:
        return self._parent_vmt

    @property
    def table_list(self) -> Mapping[int, str]:
        return self._table_list

    @property
    def virtual_methods(self) -> Mapping[int, str]:
        return self._virtual_methods

    @property
    def vmt_offsets(self) -> VMTOffsets:
        return copy.copy(self._vmt_offsets)

    @property
    def start(self) -> int:
        return self._vmt_address + self._vmt_offsets.cVmtSelfPtr

    @property
    def size(self) -> int:
        end = 0  # ????
        return end - self.start

    @property
    def offset_ptr_size(self) -> int:
        return self._offset_ptr_size

    @property
    def br_offset(self) -> int:
        return self._br.offset

    ## Public API

    def seek_to_code(self, address: int) -> bool:
        if not self._is_valid_addr(address):
            return False

        self._br.seek(address)
        return True

    def seek_to_code_offset(self, offset: int) -> bool:
        if not self._is_valid_addr(self._analyzer.start + offset):
            return False

        self._br.seek(self._analyzer.start + offset)
        return True

    def seek_to_vmt_offset(self, offset: int) -> bool:
        if not self._is_valid_addr(self._vmt_address + offset):
            return False

        self._br.seek(self._vmt_address + offset)
        return True

    def read8(self) -> Union[None, int]:
        return self._br.read8()

    def read_ptr(self) -> Union[None, int]:
        if self._offset_ptr_size == 4:
            return self._br.read32()
        elif self._offset_ptr_size == 8:
            return self._br.read64()

    ## Protected methods

    def _check_self_ptr(self) -> bool:
        if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtSelfPtr):
            return False

        self_ptr = self.read_ptr()
        return self_ptr == self._vmt_address

    def _resolve_name(self) -> bool:
        class_name_addr = self._get_class_name_addr()

        if class_name_addr is None:
            return False

        self._br.seek(class_name_addr)
        name_len = self._br.read8()

        if name_len == 0:
            BNLogger.log(
                f'Care, VMT without name (len: 0) detected at 0x{self._vmt_address:08X}',
                LogLevel.WarningLog)

        class_name = self._br.read(name_len)

        if MATCH_CLASS_NAME.match(class_name) is None:
            return False

        self._class_name = class_name.decode()
        return True

    def _resolve_instance_size(self) -> bool:
        if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtInstanceSize):
            return False

        self._instance_size = self.read_ptr()
        return True

    def _resolve_parent_vmt(self) -> bool:
        if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtParent):
            return False

        self._parent_vmt = self.read_ptr()
        return True

    def _resolve_virtual_methods(self) -> bool:
        class_name_addr = self._get_class_name_addr()

        if class_name_addr is None:
            return False

        offsets = self.vmt_offsets.__dict__.items()
        offset_map = {y: x for x, y in offsets}
        tables_addr = self._table_list.keys()

        if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtParent +
                                       self._offset_ptr_size):
            return False

        while self._br.offset < class_name_addr and self._br.offset not in tables_addr:
            field_value = self.read_ptr()

            if field_value == 0:
                continue

            if not self._is_valid_addr(field_value):
                prev_offset = self._br.offset - self._offset_ptr_size
                raise RuntimeError(
                    f'Invalid code address deteted at 0x{prev_offset:08X} '
                    '({self.class_name})\n If you think it\'s a bug, please open an issue on '
                    'Github with the used binary or the full VMT (fields + VMT) as an attachment'
                )

            field_offset = self._br.offset - self._vmt_address - self._offset_ptr_size

            if field_offset in offset_map:
                # Remove `cVmt` prefix
                method_name = f'{self.class_name}.{offset_map[field_offset][4:]}'
            else:
                method_name = f'{self.class_name}.sub_{field_value:x}'

            self._virtual_methods[field_value] = method_name

        return True

    def _resolve_table_list(self) -> bool:
        if not self.seek_to_vmt_offset(self.vmt_offsets.cVmtIntfTable):
            return False

        offsets = self._vmt_offsets.__dict__.items()
        offset_map = {y: x[4:] for x, y in offsets}

        stop_at = self._vmt_address + self._vmt_offsets.cVmtClassName

        while self._br.offset != stop_at:
            prev_br_offset = self._br.offset
            address = self.read_ptr()

            if address < 1:
                continue

            if not self._is_valid_addr(address):
                raise RuntimeError('Invalid table address detected')

            self._table_list[address] = offset_map[prev_br_offset -
                                                   self._vmt_address]

        return True

    def _is_valid_addr(self, addy: int, allow_null=False) -> bool:
        if addy == 0:
            return allow_null

        return addy >= self._analyzer.start and addy < self._analyzer.end

    def _get_class_name_addr(self) -> Union[None, int]:
        if not self.seek_to_vmt_offset(self._vmt_offsets.cVmtClassName):
            return None

        class_name_addr = self.read_ptr()

        if not self._is_valid_addr(class_name_addr):
            return None

        return class_name_addr
Ejemplo n.º 19
0
class DelphiAnalyzer(object):
    '''
    TODO: Doc
    '''
    def __init__(self,
                 bv: BinaryView,
                 delphi_version: int,
                 offset_ptr_size=-1,
                 start=-1,
                 end=-1):
        self._offset_ptr_size = offset_ptr_size if offset_ptr_size > 0 else bv.address_size
        self._start = start if start >= 0 else bv.start
        self._end = end if end > 0 else bv.end
        self._vmt_list: List[DelphiVMT] = []
        self._bv = bv
        self._br = BinaryReader(bv)
        self._delphi_version = delphi_version
        self._vmt_offsets = VMTOffsets(delphi_version, self._offset_ptr_size)

        # Not really sure about this but it's working on my test binary (Win64 Delphi v10.3)
        # Need to check offsets for macOS
        # I'll clean that later
        if self._bv.view_type == 'PE' and self._offset_ptr_size == 8:
            for x, y in self._vmt_offsets.__dict__.items():
                setattr(self._vmt_offsets, x, y + -24)

    ## Properties

    @property
    def start(self) -> int:
        return self._start

    @property
    def end(self) -> int:
        return self._end

    @property
    def delphi_version(self) -> int:
        return self._delphi_version

    @property
    def vmt_list(self) -> List[DelphiVMT]:
        return self._vmt_list

    @property
    def vmt_offsets(self) -> VMTOffsets:
        return copy.copy(self._vmt_offsets)

    ## Public API

    def update_analysis_and_wait(self,
                                 callback: Callable[[DelphiVMT], None] = None):
        self._vmt_list = []
        self._seek_to_offset(0)

        while True:
            addy = self._get_possible_vmt()

            if addy is None:
                break

            delphi_vmt = DelphiVMT(self._bv, self._delphi_version, addy, self,
                                   self._offset_ptr_size)

            if not delphi_vmt.is_valid:
                continue

            self._vmt_list.append(delphi_vmt)

            if callback is not None:
                callback(delphi_vmt)

    ## Protected methods

    def _seek_to_offset(self, offset: int):
        self._br.seek(self._start + offset)

    def _read_ptr(self) -> Union[None, int]:
        if self._offset_ptr_size == 4:
            return self._br.read32()
        elif self._offset_ptr_size == 8:
            return self._br.read64()

    def _get_possible_vmt(self) -> int:
        while self._br.offset <= self._end - self._offset_ptr_size - 1:
            begin = self._br.offset

            if not self._bv.is_valid_offset(begin):
                self._br.seek_relative(self._offset_ptr_size)
                continue

            class_vmt = self._read_ptr()

            if class_vmt is None:
                # If BinaryReader can't read, it will not update the offset
                self._br.seek_relative(self._offset_ptr_size)
                continue

            if begin == class_vmt + self._vmt_offsets.cVmtSelfPtr:
                return class_vmt
Ejemplo n.º 20
0
class UEFIHelper(BackgroundTaskThread):
    """Class for analyzing UEFI firmware to automate GUID annotation, segment fixup, type imports, and more
    """
    def __init__(self, bv: BinaryView):
        BackgroundTaskThread.__init__(self, '', False)
        self.bv = bv
        self.br = BinaryReader(self.bv)
        self.dirname = os.path.dirname(os.path.abspath(__file__))
        self.guids = self._load_guids()

    def _fix_segments(self):
        """UEFI modules run during boot, without page protections. Everything is RWX despite that the PE is built with
        the segments not being writable. It needs to be RWX so calls through global function pointers are displayed
        properly.
        """

        for seg in self.bv.segments:
            # Make segment RWX
            self.bv.add_user_segment(
                seg.start, seg.data_length, seg.data_offset, seg.data_length,
                SegmentFlag.SegmentWritable | SegmentFlag.SegmentReadable
                | SegmentFlag.SegmentExecutable)

            # Make section semantics ReadWriteDataSectionSemantics
            for section in self.bv.get_sections_at(seg.start):
                self.bv.add_user_section(
                    section.name, section.end - section.start,
                    SectionSemantics.ReadWriteDataSectionSemantics)

    def _import_types_from_headers(self):
        """Parse EDKII types from header files
        """

        hdrs_path = os.path.join(self.dirname, 'headers')
        headers = glob.glob(os.path.join(hdrs_path, '*.h'))
        for hdr in headers:
            _types = self.bv.platform.parse_types_from_source_file(hdr)
            for name, _type in _types.types.items():
                self.bv.define_user_type(name, _type)

    def _set_entry_point_prototype(self):
        """Apply correct prototype to the module entry point
        """

        _start = self.bv.get_function_at(self.bv.entry_point)
        if self.bv.view_type != 'TE':
            _start.function_type = "EFI_STATUS ModuleEntryPoint(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)"

    def _load_guids(self):
        """Read known GUIDs from CSV and convert string GUIDs to bytes

        :return: Dictionary containing GUID bytes and associated names
        """

        guids_path = os.path.join(self.dirname, 'guids.csv')
        with open(guids_path) as f:
            reader = csv.reader(f, skipinitialspace=True)
            guids = dict(reader)

        # Convert to bytes for faster lookup
        guid_bytes = dict()
        for guid, name in guids.items():
            guid_bytes[name] = uuid.UUID(guid).bytes_le

        return guid_bytes

    def _apply_guid_name_if_data(self, name: str, address: int):
        """Check if there is a function at the address. If not, then apply the EFI_GUID type and name it

        :param name: Name/symbol to apply to the GUID
        :param address: Address of the GUID
        """

        print(
            f'Found {name} at 0x{hex(address)} ({uuid.UUID(bytes_le=self.guids[name])})'
        )

        # Just to avoid a unlikely false positive and screwing up disassembly
        if self.bv.get_functions_at(address) != []:
            print(
                f'There is code at {address}, not applying GUID type and name')
            return

        self.bv.define_user_symbol(
            Symbol(SymbolType.DataSymbol, address, 'g' + name))
        t = self.bv.parse_type_string("EFI_GUID")
        self.bv.define_user_data_var(address, t[0])

    def _find_known_guids(self):
        """Search for known GUIDs and apply names to matches not within a function
        """

        names_list = list(self.guids.keys())
        guids_list = list(self.guids.values())

        def _check_guid_and_get_name(guid):
            try:
                return names_list[guids_list.index(guid)]
            except ValueError:
                return None

        for seg in self.bv.segments:
            for i in range(seg.start, seg.end):
                self.br.seek(i)
                data = self.br.read(16)
                if not data or len(data) != 16:
                    continue

                found_name = _check_guid_and_get_name(data)
                if found_name:
                    self._apply_guid_name_if_data(found_name, i)

    def _set_if_uefi_core_type(self, instr: HighLevelILInstruction):
        """Using HLIL, scrutinize the instruction to determine if it's a move of a local variable to a global variable.
        If it is, check if the source operand type is a UEFI core type and apply the type to the destination global
        variable.

        :param instr: High level IL instruction object
        """

        if instr.operation != HighLevelILOperation.HLIL_ASSIGN:
            return

        if instr.dest.operation != HighLevelILOperation.HLIL_DEREF:
            return

        if instr.dest.src.operation != HighLevelILOperation.HLIL_CONST_PTR:
            return

        if instr.src.operation != HighLevelILOperation.HLIL_VAR:
            return

        _type = instr.src.var.type
        if len(_type.tokens) == 1 and str(_type.tokens[0]) == 'EFI_HANDLE':
            self.bv.define_user_symbol(
                Symbol(SymbolType.DataSymbol, instr.dest.src.constant,
                       'gHandle'))
        elif len(_type.tokens) > 2 and str(
                _type.tokens[2]) == 'EFI_BOOT_SERVICES':
            self.bv.define_user_symbol(
                Symbol(SymbolType.DataSymbol, instr.dest.src.constant, 'gBS'))
        elif len(_type.tokens) > 2 and str(
                _type.tokens[2]) == 'EFI_RUNTIME_SERVICES':
            self.bv.define_user_symbol(
                Symbol(SymbolType.DataSymbol, instr.dest.src.constant, 'gRS'))
        elif len(_type.tokens) > 2 and str(
                _type.tokens[2]) == 'EFI_SYSTEM_TABLE':
            self.bv.define_user_symbol(
                Symbol(SymbolType.DataSymbol, instr.dest.src.constant, 'gST'))
        elif len(_type.tokens) == 1 and str(
                _type.tokens[0]) == 'EFI_PEI_FILE_HANDLE':
            self.bv.define_user_symbol(
                Symbol(SymbolType.DataSymbol, instr.dest.src.constant,
                       'gHandle'))
        elif len(_type.tokens) > 2 and str(
                _type.tokens[2]) == 'EFI_PEI_SERVICES':
            self.bv.define_user_symbol(
                Symbol(SymbolType.DataSymbol, instr.dest.src.constant,
                       'gPeiServices'))
        else:
            return

        self.bv.define_user_data_var(instr.dest.src.constant,
                                     instr.src.var.type)
        print(
            f'Found global assignment - offset:0x{hex(instr.dest.src.constant)} type:{instr.src.var.type}'
        )

    def _check_and_prop_types_on_call(self, instr: HighLevelILInstruction):
        """Most UEFI modules don't assign globals in the entry function and instead call a initialization routine and
        pass the system table to it where global assignments are made. This function ensures that the types are applied
        to the initialization function params so that we can catch global assignments outside of the module entry

        :param instr: High level IL instruction object
        """

        if instr.operation not in [
                HighLevelILOperation.HLIL_TAILCALL,
                HighLevelILOperation.HLIL_CALL
        ]:
            return

        if instr.dest.operation != HighLevelILOperation.HLIL_CONST_PTR:
            return

        argv_is_passed = False
        for arg in instr.params:
            if 'ImageHandle' in str(
                    arg
            ) or 'SystemTable' or 'FileHandle' or 'PeiServices' in str(arg):
                argv_is_passed = True
                break

        if not argv_is_passed:
            return

        func = self.bv.get_function_at(instr.dest.constant)
        old = func.function_type
        call_args = instr.params
        new_params = []
        for arg, param in zip(call_args, old.parameters):
            if hasattr(arg, 'var'):
                new_type = arg.var.type
            else:
                new_type = param.type
            new_type.confidence = 256
            new_params.append(FunctionParameter(new_type, param.name))

        # TODO: this is a hack to account for odd behavior. func.function_type should be able to set directly to
        # Type.Function(...). However, during testing this isn't the case. I am only able to get it to work if I
        # set function_type to a string and update analysis.
        gross_hack = str(
            Type.function(old.return_value, new_params, old.calling_convention,
                          old.has_variable_arguments,
                          old.stack_adjustment)).replace(
                              '(', '{}('.format(func.name))
        try:
            func.function_type = gross_hack
            self.bv.update_analysis_and_wait()
        except SyntaxError:
            pass  # BN can't parse int48_t and other types despite that it uses it. Ran into this from a sidt instruction

    def _set_global_variables(self):
        """On entry, UEFI modules usually set global variables for EFI_BOOT_SERVICES, EFI_RUNTIME_SERIVCES, and
        EFI_SYSTEM_TABLE. This function attempts to identify these assignments and apply types.
        """

        func = self.bv.get_function_at(self.bv.entry_point)
        for block in func.high_level_il:
            for instr in block:
                self._check_and_prop_types_on_call(instr)

        for func in self.bv.functions:
            for block in func.high_level_il:
                for instr in block:
                    self._set_if_uefi_core_type(instr)

    def run(self):
        """Run the task in the background
        """

        self.progress = "UEFI Helper: Fixing up segments, applying types, and searching for known GUIDs ..."
        self._fix_segments()
        self._import_types_from_headers()
        self._set_entry_point_prototype()
        self._find_known_guids()
        self.progress = "UEFI Helper: searching for global assignments for UEFI core services ..."
        self._set_global_variables()
        print('UEFI Helper completed successfully!')
Ejemplo n.º 21
0
 def __init__(self, bv: BinaryView):
     BackgroundTaskThread.__init__(self, '', False)
     self.bv = bv
     self.br = BinaryReader(self.bv)
     self.dirname = os.path.dirname(os.path.abspath(__file__))
     self.guids = self._load_guids()
Ejemplo n.º 22
0
 def __init__(self, bv):
     BackgroundTaskThread.__init__(self, "", True)
     self.progress = 'genesis: Enumerating call tables...'
     self.bv = bv
     self.br = BinaryReader(self.bv)
Ejemplo n.º 23
0
    def get_instruction_low_level_il(self, data, addr, il):
        opcode, length = self.parse_instruction(data, addr)

        op = opcodes[opcode]

        if addr == 0x10000:
            il.append(
                il.set_reg(4, 'ptr', il.const(1, 0))
            )

        if op == "Right":
            il.append(
                il.set_reg(4, 'ptr', il.add(
                    4, il.reg(4, 'ptr'), il.const(1, 1)), None)
            )
        elif op == "Left":
            il.append(
                il.set_reg(4, 'ptr', il.sub(
                    4, il.reg(4, 'ptr'), il.const(1, 1)), None)
            )
        elif op == "Add":
            il.append(
                il.store(1, il.reg(4, 'ptr'), il.add(
                    1, il.load(1, il.reg(4, 'ptr')), il.const(1, 1)), None)
            )
        elif op == "Subtract":
            il.append(
                il.store(1, il.reg(4, 'ptr'), il.sub(
                    1, il.load(1, il.reg(4, 'ptr')), il.const(1, 1)), None)
            )
        elif op == "In":
            il.append(
                il.unimplemented()
            )
        elif op == "Out":
            il.append(
                il.unimplemented()
            )
        elif op == "Open":
            true_label = il.get_label_for_address(
                Architecture['Brainfuck'], addr + 1)

            br = BinaryReader(il._source_function._view)
            br.seek(addr+1)
            # print("Found Open at : ", br.offset-1)
            counter = 1
            while counter != 0:
                instr = opcodes[br.read8()]
                if instr == "Open":
                    counter += 1
                elif instr == "Close":
                    counter -= 1
                    if counter == 0:
                        false_label = il.get_label_for_address(
                            Architecture['Brainfuck'], br.offset)
                        # print("Found loop close at offset : ", br.offset-1)
                        break
                elif br.offset == il._source_function._view.end:
                    print("Unfinished loop! This should never happen!")
                    return

            il.append(
                il.if_expr(il.compare_not_equal(1, il.load(
                    1, il.reg(4, 'ptr')), il.const(1, 0)), true_label, false_label)
            )
        elif op == "Close":
            false_label = il.get_label_for_address(
                Architecture['Brainfuck'], addr + 1)

            br = BinaryReader(il._source_function._view)
            br.seek(addr)
            # print("Found Close at : ", br.offset)
            counter = 1
            while counter != 0:
                br.seek_relative(-2)
                instr = opcodes[br.read8()]
                if instr == "Close":
                    counter += 1
                elif instr == "Open":
                    counter -= 1
                    if counter == 0:
                        true_label = il.get_label_for_address(
                            Architecture['Brainfuck'], br.offset)
                        # print("Found loop Open at offset : ", br.offset-1)
                        break
                elif br.offset == il._source_function._view.end:
                    print("Unfinished loop! This should never happen!")
                    return

            il.append(
                il.if_expr(il.compare_not_equal(1, il.load(
                    1, il.reg(4, 'ptr')), il.const(1, 0)), true_label, false_label)
            )
        else:
            il.append(
                il.nop()
            )

        return length
Ejemplo n.º 24
0
class YaraScan(BackgroundTaskThread):
    def __init__(self, bv, filepath=None, directories=None):
        self.progress_banner = "Running YARA scan"
        BackgroundTaskThread.__init__(self, self.progress_banner, True)

        self.bv = bv
        self.reader = BinaryReader(self.bv)
        self.rules = []
        self.results = []

        # Ensure that the tag types exist before using it
        if "YARA Matches" not in bv.tag_types:
            bv.create_tag_type("YARA Matches", "🔎")

        if filepath:
            self.load_signature(filepath)

        if directories:
            self.load_signatures(directories)

    def run(self):
        log_info("Scanning binary view for matching YARA signatures")

        # TODO: Scan the raw binary data from the Raw view instead of by segment.
        # This would require mapping the addresses from the Raw view to the PE/ELF views.
        # raw = self.bv.get_view_of_type("Raw")
        # reader = BinaryReader(raw)
        # data = reader.read(raw.end)

        try:
            for idx, rule in enumerate(self.rules):
                if len(self.bv.segments) == 0:
                    # Scan binary without segments
                    self.scan(self.bv.start, self.bv.end, rule)
                else:
                    # Scan by segment
                    for segment in self.bv.segments:
                        if self.cancelled:
                            return

                        self.scan(segment.start, segment.data_length, rule)

                self.progress = f"{self.progress_banner} matching on rules ({round((idx / len(self.rules)) * 100)}%)"

        except yara.TimeoutError:
            log_warn(
                "YARA scan exceeded timeout limit. Consider changing the timeout in settings."
            )
        except yara.Error as err:
            log_error("Error matching on YARA rules: {}".format(str(err)))
            show_message_box("Error",
                             "Check logs for details",
                             icon=MessageBoxIcon.ErrorIcon)

        if 0 < len(self.results):
            if Settings().get_bool("yara.displayReport"):
                self.display_report()
        else:
            log_info("YARA scan finished with no matches.")

    def scan(self, start, length, rule):
        self.reader.seek(start)
        data = self.reader.read(length)

        matches = rule.match(data=data,
                             timeout=Settings().get_integer("yara.timeout"))

        for match in matches:
            name = match.rule

            # Include the description field if its present in the metadata
            try:
                description = f"{name}: {match.meta['description']}"
            except KeyError:
                description = f"{name}"

            tag = self.bv.create_tag(self.bv.tag_types["YARA Matches"],
                                     description, True)

            for address, var, value in match.strings:
                address += start

                # Display data values correctly in the report
                if value.isascii():
                    value = value.decode("ascii")
                elif self.bv.endianness == Endianness.BigEndian:
                    value = hex(int.from_bytes(value, "big"))
                else:
                    value = hex(int.from_bytes(value, "little"))

                self.results.append({
                    "address": address,
                    "name": name,
                    "string": var,
                    "value": value
                })

                # Add either an address or data tag to the location
                funcs = self.bv.get_functions_containing(address)
                if 0 < len(funcs):
                    # Ensure the tag is not placed in the middle of an instruction
                    address = get_instruction_containing(funcs[0], address)
                    funcs[0].add_user_address_tag(address, tag)
                else:
                    self.bv.add_user_data_tag(address, tag)

    def display_report(self):
        contents = """# YARA Results

| Address | Name | String | Value |
|---------|------|--------|-------|
"""

        for result in self.results:
            contents += "| [0x{:x}](binaryninja://?expr=0x{:x}) | {} | {} | {} |\n".format(
                result["address"], result["address"], result["name"],
                result["string"], result["value"])

        self.bv.show_markdown_report("YARA Results", contents)

    def load_signature(self, filepath):
        if os.path.isfile(filepath):
            try:
                self.rules.append(yara.compile(filepath))
                log_info("Loaded YARA rule: {}".format(filepath))
            except yara.SyntaxError:
                log_error(
                    "Syntax error compiling YARA rule: {}".format(filepath))
        else:
            log_error("YARA rule filepath is invalid: {}".format(filepath))

    def load_signatures(self, directories):
        rule_files = []

        for directory in directories:
            if not os.path.isdir(directory) and directory != "":
                log_error(
                    "YARA rule directory is invalid: {}".format(directory))
            else:
                for f in os.listdir(directory):
                    if f.lower().endswith((".yar", ".yara")):
                        rule_files.append(directory + os.sep + f)

        for f in rule_files:
            try:
                self.rules.append(yara.compile(f))
                log_info("Loaded YARA rule: {}".format(f))
            except yara.SyntaxError:
                log_error("Syntax error compiling YARA rule: {}".format(f))
Ejemplo n.º 25
0
class SymbolicExecutor(object):
    def __init__(self, view, addr):

        self.view = view
        self.bw = BinaryWriter(view)
        self.br = BinaryReader(view)
        self.visitor = SymbolicVisitor(self)
        self.bncache = BNCache(view)
        self.vars = set()
        self.fringe = Fringe()
        self.ip = addr
        self.llil_ip = None
        self.arch = None
        self.user_hooks = dict()
        self.user_loggers = dict()
        self.imported_functions, self.imported_addresses = \
            get_imported_functions_and_addresses(view)
        self._last_colored_ip = None
        self._last_error = None
        self.init_with_zero = self.bncache.get_setting(
            "init_reg_mem_with_zero") == "true"

        self._wasjmp = False

        self.arch = find_arch(self.view)
        page_size = int(self.bncache.get_setting("memory.page_size"))
        self.state = State(self,
                           arch=self.arch,
                           os=find_os(view),
                           page_size=page_size)

        # load memory
        print("loading segments...")
        for segment in self.view.segments:
            start = segment.start
            end = segment.end
            size = segment.data_length
            print(segment, hex(start), "->", hex(size))

            if size == 0 and end - start != 0:
                size = end - start
                data = b"\x00" * size
            elif size == 0:
                continue
            else:
                self.br.seek(start)
                data = self.br.read(end - start)

            self.state.mem.mmap(
                self.state.address_page_aligned(start),
                self.state.address_page_aligned(end +
                                                self.state.mem.page_size - 1) -
                self.state.address_page_aligned(start),
                InitData(data, start - self.state.address_page_aligned(start)))
        print("loading finished!")

        current_function = self.bncache.get_function(addr)

        # initialize stack

        stack_page_size = int(self.bncache.get_setting("stack_size"))

        unmapped_page_init = self.state.mem.get_unmapped(
            stack_page_size, start_from=(0x80 << (self.arch.bits() - 8)))
        self.state.mem.mmap(unmapped_page_init * self.state.page_size,
                            self.state.page_size * stack_page_size)
        # leave one page for upper stack portion
        p = unmapped_page_init + stack_page_size - 1
        stack_base = p * self.state.page_size - self.arch.bits() // 8

        self.state.initialize_stack(stack_base)

        # initialize registers
        for reg in self.arch.regs_data():
            reg_dict = self.arch.regs_data()[reg]
            val = current_function.get_reg_value_after(addr, reg)

            if val.type.value == RegisterValueType.StackFrameOffset:
                setattr(self.state.regs, reg,
                        BVV(stack_base + val.offset, reg_dict['size'] * 8))
            elif (val.type.value == RegisterValueType.ConstantPointerValue
                  or val.type.value == RegisterValueType.ConstantValue):
                setattr(self.state.regs, reg,
                        BVV(val.value, reg_dict['size'] * 8))
            else:
                if not self.init_with_zero:
                    symb = BVS(reg + "_init", reg_dict['size'] * 8)
                    self.vars.add(symb)
                    setattr(self.state.regs, reg, symb)
                else:
                    setattr(self.state.regs, reg, BVV(0, reg_dict['size'] * 8))

        # initialize known local variables
        stack_vars = current_function.stack_layout
        for var in stack_vars:
            offset = var.storage
            s_type = var.type

            if abs(offset) > self.state.page_size * (stack_page_size - 1):
                print("ERROR: not enough space in stack. Increase stack size")
                raise Exception(
                    "Not enough space in stack. Increase stack size")

            if s_type.confidence != 255:
                continue

            width = s_type.width
            name = var.name
            val = current_function.get_stack_contents_at(addr, offset, width)
            if val.type.value == RegisterValueType.StackFrameOffset:
                assert width * 8 == self.arch.bits()  # has to happen... right?
                self.state.mem.store(BVV(stack_base + offset,
                                         self.arch.bits()),
                                     BVV(stack_base + val.offset, width * 8),
                                     endness=self.arch.endness())
            elif (val.type.value == RegisterValueType.ConstantPointerValue
                  or val.type.value == RegisterValueType.ConstantValue):
                self.state.mem.store(BVV(stack_base + offset,
                                         self.arch.bits()),
                                     BVV(val.value, width * 8),
                                     endness=self.arch.endness())
            elif not self.init_with_zero:
                symb = BVS(name + "_init", self.arch.bits())
                self.vars.add(symb)
                self.state.mem.store(BVV(stack_base + offset,
                                         self.arch.bits()),
                                     symb,
                                     endness=self.arch.endness())

        # set eip
        self.state.set_ip(addr)
        self.llil_ip = current_function.llil.get_instruction_start(addr)

    def __str__(self):
        return "<SymExecutor id: 0x%x, %d states>" % \
            (id(self), self.fringe.num_states + 1 if self.state is not None else 0)

    def __repr__(self):
        return self.__str__()

    def put_in_deferred(self, state):
        self.fringe.add_deferred(state)

    def put_in_exited(self, state):
        self.fringe.add_exited(state)

    def put_in_unsat(self, state):
        save_unsat = self.bncache.get_setting("save_unsat") == 'true'
        if save_unsat:
            self.fringe.add_unsat(state)

    def put_in_errored(self, state, msg: str):
        self.fringe.add_errored((msg, state))

    def set_colors(self, reset=False):
        # TODO write an UI manager, this does not belong to the executor
        old_ip = self._last_colored_ip
        if old_ip is not None:
            old_func = self.bncache.get_function(old_ip)
            old_func.set_auto_instr_highlight(old_ip, NO_COLOR)

        for ip in self.fringe._deferred:
            func = self.bncache.get_function(ip)
            func.set_auto_instr_highlight(
                ip, DEFERRED_STATE_COLOR if not reset else NO_COLOR)
            # func.set_comment_at(ip, str(len(self.fringe._deferred[ip]) if not reset else ""))

        for _, state in self.fringe.errored:
            func = self.bncache.get_function(state.get_ip())
            func.set_auto_instr_highlight(
                state.get_ip(), ERRORED_STATE_COLOR if not reset else NO_COLOR)

        if self.state:
            func = self.bncache.get_function(self.ip)
            func.set_auto_instr_highlight(
                self.ip, CURR_STATE_COLOR if not reset else NO_COLOR)
        if not reset:
            self._last_colored_ip = self.ip

    def reset(self):
        self.set_colors(reset=True)

    def set_current_state(self, state):
        if self.state is not None:
            self.state.llil_ip = self.llil_ip
            self.put_in_deferred(self.state)
            self.state = None

        ip = state.get_ip()
        llil_ip = state.llil_ip

        self.state = state
        new_func = self.bncache.get_function(ip)
        self.ip = ip
        self.llil_ip = new_func.llil.get_instruction_start(
            ip) if llil_ip is None else llil_ip

    def select_from_deferred(self):
        if self.fringe.is_empty():
            return False

        state = self.fringe.get_one_deferred()
        self.set_current_state(state)
        return True

    def update_ip(self, funcion_name, new_llil_ip):
        self.llil_ip = new_llil_ip
        self.ip = self.bncache.get_address(funcion_name, new_llil_ip)
        self.state.set_ip(self.ip)
        self.state.llil_ip = new_llil_ip

    def _execute_one(self):
        self._last_error = None
        func_name = self.bncache.get_function_name(self.ip)

        # handle user hooks and loggers
        if self.ip in self.user_loggers:
            self.user_loggers[self.ip](self.state)
        if self.ip in self.user_hooks:
            old_ip = self.ip
            new_state, new_deferred, new_errored = self.user_hooks[self.ip](
                self.state)

            for s in new_deferred:
                self.put_in_deferred(s)
            for s, msg in new_errored:
                self.put_in_errored(s, msg)

            if new_state is not None:
                self.state = new_state

                if old_ip == self.state.get_ip():
                    new_ip = self.ip + \
                        self.bncache.get_instruction_len(self.ip)
                else:
                    new_ip = self.state.get_ip()

                dest_func_name = self.bncache.get_function_name(new_ip)
                self.update_ip(
                    dest_func_name,
                    self.bncache.get_llil_address(dest_func_name, new_ip))
                return self.ip

        else:
            # check if a special handler is defined

            dont_use_special_handlers = \
                self.bncache.get_setting("dont_use_special_handlers") == 'true'
            disasm_str = self.bncache.get_disasm(self.ip)

            try:
                if (dont_use_special_handlers
                        or not self.arch.execute_special_handler(
                            disasm_str, self)):
                    expr = self.bncache.get_llil(func_name, self.llil_ip)
                    self.visitor.visit(expr)
                else:
                    self._wasjmp = True
                    self.ip = self.ip + \
                        self.view.get_instruction_length(self.ip)
                    self.state.set_ip(self.ip)
                    self.llil_ip = self.bncache.get_function(
                        self.ip).llil.get_instruction_start(self.ip)
            except exceptions.ExitException:
                self.put_in_exited(self.state)
                self.state = None
            except exceptions.SENinjaError as err:
                print("An error occurred: %s" % err.message)
                self.state = None
                self._last_error = err
                if err.is_fatal():
                    raise err

        if self.state is None:
            if self.fringe.is_empty():
                print("WARNING: no more states")
                return -1
            else:
                self.select_from_deferred()
                self._wasjmp = True

        if not self._wasjmp:
            # go on by 1 instruction
            self.update_ip(func_name, self.llil_ip + 1)
        else:
            self._wasjmp = False

        return self.ip

    def execute_one(self):
        if not self.state:
            return

        single_llil_step = self.bncache.get_setting(
            "single_llil_step") == 'true'
        if single_llil_step:
            res = self._execute_one()
        else:
            old_ip = self.ip
            res = old_ip
            while res == old_ip:
                res = self._execute_one()
        return res
Ejemplo n.º 26
0
    def __init__(self, view, addr):

        self.view = view
        self.bw = BinaryWriter(view)
        self.br = BinaryReader(view)
        self.visitor = SymbolicVisitor(self)
        self.bncache = BNCache(view)
        self.vars = set()
        self.fringe = Fringe()
        self.ip = addr
        self.llil_ip = None
        self.arch = None
        self.user_hooks = dict()
        self.user_loggers = dict()
        self.imported_functions, self.imported_addresses = \
            get_imported_functions_and_addresses(view)
        self._last_colored_ip = None
        self._last_error = None
        self.init_with_zero = self.bncache.get_setting(
            "init_reg_mem_with_zero") == "true"

        self._wasjmp = False

        self.arch = find_arch(self.view)
        page_size = int(self.bncache.get_setting("memory.page_size"))
        self.state = State(self,
                           arch=self.arch,
                           os=find_os(view),
                           page_size=page_size)

        # load memory
        print("loading segments...")
        for segment in self.view.segments:
            start = segment.start
            end = segment.end
            size = segment.data_length
            print(segment, hex(start), "->", hex(size))

            if size == 0 and end - start != 0:
                size = end - start
                data = b"\x00" * size
            elif size == 0:
                continue
            else:
                self.br.seek(start)
                data = self.br.read(end - start)

            self.state.mem.mmap(
                self.state.address_page_aligned(start),
                self.state.address_page_aligned(end +
                                                self.state.mem.page_size - 1) -
                self.state.address_page_aligned(start),
                InitData(data, start - self.state.address_page_aligned(start)))
        print("loading finished!")

        current_function = self.bncache.get_function(addr)

        # initialize stack

        stack_page_size = int(self.bncache.get_setting("stack_size"))

        unmapped_page_init = self.state.mem.get_unmapped(
            stack_page_size, start_from=(0x80 << (self.arch.bits() - 8)))
        self.state.mem.mmap(unmapped_page_init * self.state.page_size,
                            self.state.page_size * stack_page_size)
        # leave one page for upper stack portion
        p = unmapped_page_init + stack_page_size - 1
        stack_base = p * self.state.page_size - self.arch.bits() // 8

        self.state.initialize_stack(stack_base)

        # initialize registers
        for reg in self.arch.regs_data():
            reg_dict = self.arch.regs_data()[reg]
            val = current_function.get_reg_value_after(addr, reg)

            if val.type.value == RegisterValueType.StackFrameOffset:
                setattr(self.state.regs, reg,
                        BVV(stack_base + val.offset, reg_dict['size'] * 8))
            elif (val.type.value == RegisterValueType.ConstantPointerValue
                  or val.type.value == RegisterValueType.ConstantValue):
                setattr(self.state.regs, reg,
                        BVV(val.value, reg_dict['size'] * 8))
            else:
                if not self.init_with_zero:
                    symb = BVS(reg + "_init", reg_dict['size'] * 8)
                    self.vars.add(symb)
                    setattr(self.state.regs, reg, symb)
                else:
                    setattr(self.state.regs, reg, BVV(0, reg_dict['size'] * 8))

        # initialize known local variables
        stack_vars = current_function.stack_layout
        for var in stack_vars:
            offset = var.storage
            s_type = var.type

            if abs(offset) > self.state.page_size * (stack_page_size - 1):
                print("ERROR: not enough space in stack. Increase stack size")
                raise Exception(
                    "Not enough space in stack. Increase stack size")

            if s_type.confidence != 255:
                continue

            width = s_type.width
            name = var.name
            val = current_function.get_stack_contents_at(addr, offset, width)
            if val.type.value == RegisterValueType.StackFrameOffset:
                assert width * 8 == self.arch.bits()  # has to happen... right?
                self.state.mem.store(BVV(stack_base + offset,
                                         self.arch.bits()),
                                     BVV(stack_base + val.offset, width * 8),
                                     endness=self.arch.endness())
            elif (val.type.value == RegisterValueType.ConstantPointerValue
                  or val.type.value == RegisterValueType.ConstantValue):
                self.state.mem.store(BVV(stack_base + offset,
                                         self.arch.bits()),
                                     BVV(val.value, width * 8),
                                     endness=self.arch.endness())
            elif not self.init_with_zero:
                symb = BVS(name + "_init", self.arch.bits())
                self.vars.add(symb)
                self.state.mem.store(BVV(stack_base + offset,
                                         self.arch.bits()),
                                     symb,
                                     endness=self.arch.endness())

        # set eip
        self.state.set_ip(addr)
        self.llil_ip = current_function.llil.get_instruction_start(addr)
Ejemplo n.º 27
0
class HexView():

  def __init__(self, bnc):
    self.bnc = bnc

    self.br = BinaryReader(bnc.bv)

    self.hexScreen = curses.newpad(curses.LINES-1, curses.COLS)
    self.hexOffset = 0
    self.hexCursor = bnc.pos
    self.loadHexLines()

  def loadHexLines(self):
    # Get lines to render
    self.hexLines = []
    topOfScreen = (self.bnc.pos- (self.bnc.pos% self.bnc.program.settings["hexLineLength"])) - (self.hexOffset * self.bnc.program.settings["hexLineLength"])
    self.topOfScreen = topOfScreen
    self.br.seek(topOfScreen)
    # TODO...make this for loop at least reasonably efficient...it's seriously just a clusterfuck right now
    for _ in range(self.hexScreen.getmaxyx()[0]-2):
      offset = "{:08x}   ".format(self.br.offset)
      line_bytes = self.br.read(self.bnc.program.settings["hexLineLength"])
      
      # Sections that don't exist in the memory
      if line_bytes is None:
        line_bytes = b''
        line_byte = self.br.read(1)
        while line_byte is not None:
          line_bytes += line_byte
          line_byte = self.br.read(1)

      if len(line_bytes) == self.bnc.program.settings["hexLineLength"]:
        byteValues = ''.join(["{:02x} ".format(b) for b in line_bytes])[:-1]
        asciiValues = ''.join([chr(int(b, 16)) if (int(b, 16) > 31 and int(b, 16) < 127) else '.' for b in byteValues.split(' ')])
        self.hexLines.append(offset + byteValues + "   " + asciiValues)

      else:
        byteValues = ''.join(["{:02x} ".format(b) for b in line_bytes])[:-1]
        asciiValues = ''.join([chr(int(b, 16)) if (int(b, 16) > 31 and int(b, 16) < 127) else '.' for b in byteValues.split(' ')[:-1]])
        self.hexLines.append(offset + byteValues + " "*(self.bnc.program.settings["hexLineLength"]*3-len(byteValues)) + "  " + asciiValues)
        if (len(self.hexLines) != self.hexScreen.getmaxyx()[0]-2):
          self.hexLines.append('-'*(self.bnc.program.settings["hexLineLength"]*4 + 13))

        line_byte = None
        while line_byte is None and self.br.offset <= self.bnc.bv.end:
          self.br.seek(self.br.offset+1)
          line_byte = self.br.read(1)
        self.br.seek(self.br.offset-1)

      if (len(self.hexLines) == self.hexScreen.getmaxyx()[0]-2):
        break

  def parseInput(self, key):
    # Scroll
    if key == self.bnc.program.settings["hexViewRight"]:
      self.hexCursor += 0.5

      if self.hexCursor%1.0 == 0:
        self.bnc.pos+= 1
        if self.bnc.pos% self.bnc.program.settings["hexLineLength"] == 0:
          self.hexOffset += 1

    elif key == self.bnc.program.settings["hexViewLeft"]:
      self.hexCursor -= 0.5

      if self.hexCursor%1.0 == 0.5:
        if self.bnc.pos% self.bnc.program.settings["hexLineLength"] == 0:
          self.hexOffset -= 1
        self.bnc.pos-= 1

    elif key == self.bnc.program.settings["hexViewLineDown"]:
      self.hexOffset += 1
      self.bnc.pos+= self.bnc.program.settings["hexLineLength"]
      self.hexCursor += self.bnc.program.settings["hexLineLength"]
    elif key == self.bnc.program.settings["hexViewLineUp"]:
      self.hexOffset -= 1
      self.bnc.pos-= self.bnc.program.settings["hexLineLength"]
      self.hexCursor -= self.bnc.program.settings["hexLineLength"]
    elif key == self.bnc.program.settings["hexViewPageDown"]:
      self.hexOffset += self.hexScreen.getmaxyx()[0]-3
      self.bnc.pos+= self.bnc.program.settings["hexLineLength"] * (self.hexScreen.getmaxyx()[0]-3)
      self.hexCursor += self.bnc.program.settings["hexLineLength"] * (self.hexScreen.getmaxyx()[0]-3)
    elif key == self.bnc.program.settings["hexViewPageUp"]:
      self.hexOffset -= self.hexScreen.getmaxyx()[0]-3
      self.bnc.pos-= self.bnc.program.settings["hexLineLength"] * (self.hexScreen.getmaxyx()[0]-3)
      self.hexCursor -= self.bnc.program.settings["hexLineLength"] * (self.hexScreen.getmaxyx()[0]-3)
    
    elif key in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']:
      if self.hexCursor % 1.0 == 0.5:
        self.bnc.bv.write(self.bnc.pos, (int(key, 16) |
          (ord(self.bnc.bv.read(self.bnc.pos, 1).decode('charmap')) & 0b11110000)).to_bytes(1, 'big'))

        self.hexCursor += 0.5
        self.bnc.pos+= 1
        if self.bnc.pos% self.bnc.program.settings["hexLineLength"] == 0:
          self.hexOffset += 1
      else:
        self.bnc.bv.write(self.bnc.pos, (int(key, 16) << 4 |
          (ord(self.bnc.bv.read(self.bnc.pos, 1).decode('charmap')) & 0b00001111)).to_bytes(1, 'big'))

        self.hexCursor += 0.5

    # Adjust for off screen
    if self.hexOffset < 0:
      self.hexOffset = 0
    elif self.hexOffset > self.hexScreen.getmaxyx()[0]-3:
      self.hexOffset = self.hexScreen.getmaxyx()[0]-3

    if self.bnc.pos< self.bnc.bv.start:
      self.bnc.pos= self.bnc.bv.start
      self.hexCursor = self.bnc.pos
    elif self.bnc.pos> self.bnc.bv.end:
      self.bnc.pos= self.bnc.bv.end
      self.hexCursor = self.bnc.pos+ 0.5

    #   if self.hexOffset == skippedLinesLine:
    # if key == self.bnc.program.settings["hexViewLineUp"] or key == self.bnc.program.settings["hexViewPageUp"]:
    #   self.hexOffset -= 1
    # if key == self.bnc.program.settings["hexViewLineDown"] or key == self.bnc.program.settings["hexViewPageDown"]:
    #   self.hexOffset += 1
    #   self.bnc.pos= self.br.offset

    self.loadHexLines()

  def render(self):
    self.hexScreen.erase()
    self.hexScreen.border()

    for yLine, rawBytes in enumerate(self.hexLines):
      if yLine == self.hexOffset:
        for xLine, rawByte in enumerate(rawBytes):
          if self.hexCursor%self.bnc.program.settings["hexLineLength"] == (xLine-8-3)//3 + ((xLine-8-3)%3)/2.0 and (xLine-8-3)%3 != 2:
            self.hexScreen.addstr(yLine+1, 2+xLine, rawByte, curses.A_STANDOUT)
          else:
            self.hexScreen.addstr(yLine+1, 2+xLine, rawByte)
      else:
        self.hexScreen.addstr(yLine+1, 2, rawBytes)

    title = "Hex"
    drawMultiLineText(0, self.hexScreen.getmaxyx()[1]-len(title)-3, title, self.hexScreen)
    self.hexScreen.noutrefresh(0, 0, 0, 0, curses.LINES-2, curses.COLS-1)