Exemple #1
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)
Exemple #2
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
Exemple #3
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)
Exemple #4
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)
Exemple #5
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)
Exemple #6
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
Exemple #7
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
Exemple #8
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