Beispiel #1
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)
Beispiel #2
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)
Beispiel #3
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
Beispiel #4
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