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.")
Exemple #2
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 #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
    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