Ejemplo n.º 1
0
    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.addr_mod = interval()

        self.options = {
            "jit_maxline": 50  # Maximum number of line jitted
        }
Ejemplo n.º 2
0
    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.options = {"jit_maxline": 50,  # Maximum number of line jitted
                        "max_exec_per_call": 0 # 0 means no limit
                        }

        self.mdis = asmblock.disasmEngine(ir_arch.arch, ir_arch.attrib, bs,
                                          lines_wd=self.options["jit_maxline"],
                                          symbol_pool=ir_arch.symbol_pool,
                                          follow_call=False,
                                          dontdis_retcall=False,
                                          split_dis=self.split_dis,
                                          dis_bloc_callback=self.disasm_cb)
Ejemplo n.º 3
0
    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.options = {"jit_maxline": 50,  # Maximum number of line jitted
                        "max_exec_per_call": 0 # 0 means no limit
                        }

        self.mdis = asmbloc.disasmEngine(ir_arch.arch, ir_arch.attrib, bs,
                                         lines_wd=self.options["jit_maxline"],
                                         symbol_pool=ir_arch.symbol_pool,
                                         follow_call=False,
                                         dontdis_retcall=False,
                                         split_dis=self.split_dis,
                                         dis_bloc_callback=self.disasm_cb)
Ejemplo n.º 4
0
    def test_boundedDict(self):
        from miasm2.core.utils import BoundedDict

        # Use a callback
        def logger(key):
            print "DELETE", key

        # Create a 5/2 dictionnary
        bd = BoundedDict(5, 2, initialdata={"element": "value"},
                         delete_cb=logger)
        bd["element2"] = "value2"
        assert("element" in bd)
        assert("element2" in bd)
        self.assertEqual(bd["element"], "value")
        self.assertEqual(bd["element2"], "value2")

        # Increase 'element2' use
        _ = bd["element2"]

        for i in xrange(6):
            bd[i] = i
            print "Insert %d -> %s" % (i, bd)

        assert(len(bd) == 2)

        assert("element2" in bd)
        self.assertEqual(bd["element2"], "value2")
Ejemplo n.º 5
0
    def __init__(self, ir_arch, bin_stream):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bin_stream: bin_stream instance
        """
        # Arch related
        self.ir_arch = ir_arch
        self.ircfg = self.ir_arch.new_ircfg()
        self.arch_name = "%s%s" % (self.ir_arch.arch.name, self.ir_arch.attrib)

        # Structures for block tracking
        self.offset_to_jitted_func = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.loc_key_to_block = {}
        self.blocks_mem_interval = interval()

        # Logging & options
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.options = {"jit_maxline": 50,  # Maximum number of line jitted
                        "max_exec_per_call": 0 # 0 means no limit
                        }

        # Disassembly Engine
        self.split_dis = set()
        self.mdis = disasmEngine(
            ir_arch.arch, ir_arch.attrib, bin_stream,
            lines_wd=self.options["jit_maxline"],
            loc_db=ir_arch.loc_db,
            follow_call=False,
            dontdis_retcall=False,
            split_dis=self.split_dis,
        )
Ejemplo n.º 6
0
    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.addr_mod = interval()

        self.options = {"jit_maxline": 50  # Maximum number of line jitted
                        }
Ejemplo n.º 7
0
    def __init__(self, ir_arch, bin_stream):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bin_stream: bin_stream instance
        """
        # Arch related
        self.ir_arch = ir_arch
        self.ircfg = self.ir_arch.new_ircfg()
        self.arch_name = "%s%s" % (self.ir_arch.arch.name, self.ir_arch.attrib)

        # Structures for block tracking
        self.offset_to_jitted_func = BoundedDict(
            self.jitted_block_max_size, delete_cb=self.jitted_block_delete_cb)
        self.loc_key_to_block = {}
        self.blocks_mem_interval = interval()

        # Logging & options
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.options = {
            "jit_maxline": 50,  # Maximum number of line jitted
            "max_exec_per_call": 0  # 0 means no limit
        }

        # Disassembly Engine
        self.split_dis = set()
        self.mdis = disasmEngine(
            ir_arch.arch,
            ir_arch.attrib,
            bin_stream,
            lines_wd=self.options["jit_maxline"],
            loc_db=ir_arch.loc_db,
            follow_call=False,
            dontdis_retcall=False,
            split_dis=self.split_dis,
        )
Ejemplo n.º 8
0
class JitCore(object):

    "JiT management. This is an abstract class"

    jitted_block_delete_cb = None
    jitted_block_max_size = 10000

    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.options = {"jit_maxline": 50,  # Maximum number of line jitted
                        "max_exec_per_call": 0 # 0 means no limit
                        }

        self.mdis = asmbloc.disasmEngine(ir_arch.arch, ir_arch.attrib, bs,
                                         lines_wd=self.options["jit_maxline"],
                                         symbol_pool=ir_arch.symbol_pool,
                                         follow_call=False,
                                         dontdis_retcall=False,
                                         split_dis=self.split_dis,
                                         dis_bloc_callback=self.disasm_cb)


    def set_options(self, **kwargs):
        "Set options relative to the backend"

        self.options.update(kwargs)

    def clear_jitted_blocks(self):
        "Reset all jitted blocks"
        self.lbl2jitbloc.clear()
        self.lbl2bloc.clear()
        self.blocs_mem_interval = interval()

    def add_disassembly_splits(self, *args):
        """The disassembly engine will stop on address in args if they
        are not at the block beginning"""
        self.split_dis.update(set(args))

    def remove_disassembly_splits(self, *args):
        """The disassembly engine will no longer stop on address in args"""
        self.split_dis.difference_update(set(args))

    def load(self):
        "Initialise the Jitter"
        raise NotImplementedError("Abstract class")

    def get_bloc_min_max(self, cur_bloc):
        "Update cur_bloc to set min/max address"

        if cur_bloc.lines:
            cur_bloc.ad_min = cur_bloc.lines[0].offset
            cur_bloc.ad_max = cur_bloc.lines[-1].offset + cur_bloc.lines[-1].l
        else:
            # 1 byte block for unknown mnemonic
            cur_bloc.ad_min = cur_bloc.label.offset
            cur_bloc.ad_max = cur_bloc.label.offset+1


    def add_bloc_to_mem_interval(self, vm, bloc):
        "Update vm to include bloc addresses in its memory range"

        self.blocs_mem_interval += interval([(bloc.ad_min, bloc.ad_max - 1)])

        vm.reset_code_bloc_pool()
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def jitirblocs(self, label, irblocs):
        """JiT a group of irblocs.
        @label: the label of the irblocs
        @irblocs: a gorup of irblocs
        """

        raise NotImplementedError("Abstract class")

    def add_bloc(self, b):
        """Add a bloc to JiT and JiT it.
        @b: the bloc to add
        """

        irblocs = self.ir_arch.add_bloc(b, gen_pc_updt = True)
        b.irblocs = irblocs
        self.jitirblocs(b.label, irblocs)

    def disbloc(self, addr, vm):
        """Disassemble a new bloc and JiT it
        @addr: address of the block to disassemble (asm_label or int)
        @vm: VmMngr instance
        """

        # Get the bloc
        if isinstance(addr, asmbloc.asm_label):
            addr = addr.offset

        # Prepare disassembler
        self.mdis.job_done.clear()
        self.mdis.lines_wd = self.options["jit_maxline"]
        self.mdis.dis_bloc_callback = self.disasm_cb

        # Disassemble it
        try:
            cur_bloc = self.mdis.dis_bloc(addr)
        except IOError:
            # vm_exception_flag is set
            label = self.ir_arch.symbol_pool.getby_offset_create(addr)
            cur_bloc = asmbloc.asm_block_bad(label)

        # Logging
        if self.log_newbloc:
            print cur_bloc

        # Update label -> bloc
        self.lbl2bloc[cur_bloc.label] = cur_bloc

        # Store min/max bloc address needed in jit automod code
        self.get_bloc_min_max(cur_bloc)

        # JiT it
        self.add_bloc(cur_bloc)

        # Update jitcode mem range
        self.add_bloc_to_mem_interval(vm, cur_bloc)

    def runbloc(self, cpu, lbl, breakpoints):
        """Run the bloc starting at lbl.
        @cpu: JitCpu instance
        @lbl: target label
        """

        if lbl is None:
            lbl = getattr(cpu, self.ir_arch.pc.name)

        if not lbl in self.lbl2jitbloc:
            # Need to JiT the bloc
            self.disbloc(lbl, cpu.vmmngr)

        # Run the bloc and update cpu/vmmngr state
        return self.exec_wrapper(lbl, cpu, self.lbl2jitbloc.data, breakpoints,
                                 self.options["max_exec_per_call"])

    def blocs2memrange(self, blocs):
        """Return an interval instance standing for blocs addresses
        @blocs: list of asm_bloc instances
        """

        mem_range = interval()

        for b in blocs:
            mem_range += interval([(b.ad_min, b.ad_max - 1)])

        return mem_range

    def __updt_jitcode_mem_range(self, vm):
        """Rebuild the VM blocs address memory range
        @vm: VmMngr instance
        """

        # Reset the current pool
        vm.reset_code_bloc_pool()

        # Add blocs in the pool
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def del_bloc_in_range(self, ad1, ad2):
        """Find and remove jitted bloc in range [ad1, ad2].
        Return the list of bloc removed.
        @ad1: First address
        @ad2: Last address
        """

        # Find concerned blocs
        modified_blocs = set()
        for b in self.lbl2bloc.values():
            if not b.lines:
                continue
            if b.ad_max <= ad1 or b.ad_min >= ad2:
                # Bloc not modified
                pass
            else:
                # Modified blocs
                modified_blocs.add(b)

        # Generate interval to delete
        del_interval = self.blocs2memrange(modified_blocs)

        # Remove interval from monitored interval list
        self.blocs_mem_interval -= del_interval

        # Remove modified blocs
        for b in modified_blocs:
            try:
                for irbloc in b.irblocs:

                    # Remove offset -> jitted bloc link
                    if irbloc.label.offset in self.lbl2jitbloc:
                        del(self.lbl2jitbloc[irbloc.label.offset])

            except AttributeError:
                # The bloc has never been translated in IR
                if b.label.offset in self.lbl2jitbloc:
                    del(self.lbl2jitbloc[b.label.offset])

            # Remove label -> bloc link
            del(self.lbl2bloc[b.label])

        return modified_blocs

    def updt_automod_code_range(self, vm, mem_range):
        """Remove jitted code in range @mem_range
        @vm: VmMngr instance
        @mem_range: list of start/stop addresses
        """
        for addr_start, addr_stop in mem_range:
            self.del_bloc_in_range(addr_start, addr_stop)
        self.__updt_jitcode_mem_range(vm)
        vm.reset_memory_access()

    def updt_automod_code(self, vm):
        """Remove jitted code updated by memory write
        @vm: VmMngr instance
        """
        mem_range = []
        for addr_start, addr_stop in vm.get_memory_write():
            mem_range.append((addr_start, addr_stop))
        self.updt_automod_code_range(vm, mem_range)
Ejemplo n.º 9
0
class JitCore(object):

    "JiT management. This is an abstract class"

    jitted_block_delete_cb = None
    jitted_block_max_size = 10000

    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.arch_name = "%s%s" % (self.ir_arch.arch.name, self.ir_arch.attrib)
        self.bs = bs
        self.known_blocs = {}
        self.loc_key_to_jit_block = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.options = {"jit_maxline": 50,  # Maximum number of line jitted
                        "max_exec_per_call": 0 # 0 means no limit
                        }

        self.mdis = disasmEngine(
            ir_arch.arch, ir_arch.attrib, bs,
            lines_wd=self.options["jit_maxline"],
            symbol_pool=ir_arch.symbol_pool,
            follow_call=False,
            dontdis_retcall=False,
            split_dis=self.split_dis,
            dis_block_callback=self.disasm_cb
        )


    def set_options(self, **kwargs):
        "Set options relative to the backend"

        self.options.update(kwargs)

    def clear_jitted_blocks(self):
        "Reset all jitted blocks"
        self.loc_key_to_jit_block.clear()
        self.lbl2bloc.clear()
        self.blocs_mem_interval = interval()

    def add_disassembly_splits(self, *args):
        """The disassembly engine will stop on address in args if they
        are not at the block beginning"""
        self.split_dis.update(set(args))

    def remove_disassembly_splits(self, *args):
        """The disassembly engine will no longer stop on address in args"""
        self.split_dis.difference_update(set(args))

    def load(self):
        "Initialise the Jitter"
        raise NotImplementedError("Abstract class")

    def get_bloc_min_max(self, cur_block):
        "Update cur_block to set min/max address"

        if cur_block.lines:
            cur_block.ad_min = cur_block.lines[0].offset
            cur_block.ad_max = cur_block.lines[-1].offset + cur_block.lines[-1].l
        else:
            # 1 byte block for unknown mnemonic
            offset = ir_arch.symbol_pool.loc_key_to_offset(cur_block.loc_key)
            cur_block.ad_min = offset
            cur_block.ad_max = offset+1


    def add_bloc_to_mem_interval(self, vm, block):
        "Update vm to include block addresses in its memory range"

        self.blocs_mem_interval += interval([(block.ad_min, block.ad_max - 1)])

        vm.reset_code_bloc_pool()
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def jitirblocs(self, label, irblocks):
        """JiT a group of irblocks.
        @label: the label of the irblocks
        @irblocks: a gorup of irblocks
        """

        raise NotImplementedError("Abstract class")

    def add_bloc(self, block):
        """Add a block to JiT and JiT it.
        @block: asm_bloc to add
        """
        irblocks = self.ir_arch.add_block(block, gen_pc_updt = True)
        block.blocks = irblocks
        self.jitirblocs(block.loc_key, irblocks)

    def disbloc(self, addr, vm):
        """Disassemble a new block and JiT it
        @addr: address of the block to disassemble (LocKey or int)
        @vm: VmMngr instance
        """

        # Get the block
        if isinstance(addr, LocKey):
            addr = self.ir_arch.symbol_pool.loc_key_to_offset(addr)
            if addr is None:
                raise RuntimeError("Unknown offset for LocKey")

        # Prepare disassembler
        self.mdis.lines_wd = self.options["jit_maxline"]
        self.mdis.dis_block_callback = self.disasm_cb

        # Disassemble it
        cur_block = self.mdis.dis_block(addr)
        if isinstance(cur_block, AsmBlockBad):
            return cur_block
        # Logging
        if self.log_newbloc:
            print cur_block.to_string(self.mdis.symbol_pool)

        # Update label -> block
        self.lbl2bloc[cur_block.loc_key] = cur_block

        # Store min/max block address needed in jit automod code
        self.get_bloc_min_max(cur_block)

        # JiT it
        self.add_bloc(cur_block)

        # Update jitcode mem range
        self.add_bloc_to_mem_interval(vm, cur_block)
        return cur_block

    def runbloc(self, cpu, lbl, breakpoints):
        """Run the block starting at lbl.
        @cpu: JitCpu instance
        @lbl: target label
        """

        if lbl is None:
            lbl = getattr(cpu, self.ir_arch.pc.name)

        if not lbl in self.loc_key_to_jit_block:
            # Need to JiT the block
            cur_block = self.disbloc(lbl, cpu.vmmngr)
            if isinstance(cur_block, AsmBlockBad):
                errno = cur_block.errno
                if errno == AsmBlockBad.ERROR_IO:
                    cpu.vmmngr.set_exception(EXCEPT_ACCESS_VIOL)
                elif errno == AsmBlockBad.ERROR_CANNOT_DISASM:
                    cpu.set_exception(EXCEPT_UNK_MNEMO)
                else:
                    raise RuntimeError("Unhandled disasm result %r" % errno)
                return lbl

        # Run the block and update cpu/vmmngr state
        return self.exec_wrapper(lbl, cpu, self.loc_key_to_jit_block.data, breakpoints,
                                 self.options["max_exec_per_call"])

    def blocs2memrange(self, blocks):
        """Return an interval instance standing for blocks addresses
        @blocks: list of asm_bloc instances
        """

        mem_range = interval()

        for block in blocks:
            mem_range += interval([(block.ad_min, block.ad_max - 1)])

        return mem_range

    def __updt_jitcode_mem_range(self, vm):
        """Rebuild the VM blocks address memory range
        @vm: VmMngr instance
        """

        # Reset the current pool
        vm.reset_code_bloc_pool()

        # Add blocks in the pool
        for start, stop in self.blocs_mem_interval:
            vm.add_code_bloc(start, stop + 1)

    def del_bloc_in_range(self, ad1, ad2):
        """Find and remove jitted block in range [ad1, ad2].
        Return the list of block removed.
        @ad1: First address
        @ad2: Last address
        """

        # Find concerned blocks
        modified_blocks = set()
        for block in self.lbl2bloc.values():
            if not block.lines:
                continue
            if block.ad_max <= ad1 or block.ad_min >= ad2:
                # Block not modified
                pass
            else:
                # Modified blocks
                modified_blocks.add(block)

        # Generate interval to delete
        del_interval = self.blocs2memrange(modified_blocks)

        # Remove interval from monitored interval list
        self.blocs_mem_interval -= del_interval

        # Remove modified blocks
        for block in modified_blocks:
            try:
                for irblock in block.blocks:
                    # Remove offset -> jitted block link
                    offset = self.ir_arch.symbol_pool.loc_key_to_offset(irblock.loc_key)
                    if offset in self.loc_key_to_jit_block:
                        del(self.loc_key_to_jit_block[offset])

            except AttributeError:
                # The block has never been translated in IR
                offset = self.ir_arch.symbol_pool.loc_key_to_offset(block.loc_key)
                if offset in self.loc_key_to_jit_block:
                    del(self.loc_key_to_jit_block[offset])

            # Remove label -> block link
            del(self.lbl2bloc[block.loc_key])

        return modified_blocks

    def updt_automod_code_range(self, vm, mem_range):
        """Remove jitted code in range @mem_range
        @vm: VmMngr instance
        @mem_range: list of start/stop addresses
        """
        for addr_start, addr_stop in mem_range:
            self.del_bloc_in_range(addr_start, addr_stop)
        self.__updt_jitcode_mem_range(vm)
        vm.reset_memory_access()

    def updt_automod_code(self, vm):
        """Remove jitted code updated by memory write
        @vm: VmMngr instance
        """
        mem_range = []
        for addr_start, addr_stop in vm.get_memory_write():
            mem_range.append((addr_start, addr_stop))
        self.updt_automod_code_range(vm, mem_range)

    def hash_block(self, block):
        """
        Build a hash of the block @block
        @block: asmblock
        """
        block_raw = "".join(line.b for line in block.lines)
        offset = self.ir_arch.symbol_pool.loc_key_to_offset(block.loc_key)
        block_hash = md5("%X_%s_%s_%s_%s" % (offset,
                                             self.arch_name,
                                             self.log_mn,
                                             self.log_regs,
                                             block_raw)).hexdigest()
        return block_hash
Ejemplo n.º 10
0
 def __init__(self, cache_size=1000):
     """Instance a translator
     @cache_size: (optional) Expr cache size
     """
     self._cache = BoundedDict(cache_size)
Ejemplo n.º 11
0
class JitCore(object):

    "JiT management. This is an abstract class"

    jitted_block_delete_cb = None
    jitted_block_max_size = 10000

    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.arch_name = "%s%s" % (self.ir_arch.arch.name, self.ir_arch.attrib)
        self.bs = bs
        self.known_blocs = {}
        self.loc_key_to_jit_block = BoundedDict(
            self.jitted_block_max_size, delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.options = {
            "jit_maxline": 50,  # Maximum number of line jitted
            "max_exec_per_call": 0  # 0 means no limit
        }

        self.mdis = disasmEngine(ir_arch.arch,
                                 ir_arch.attrib,
                                 bs,
                                 lines_wd=self.options["jit_maxline"],
                                 symbol_pool=ir_arch.symbol_pool,
                                 follow_call=False,
                                 dontdis_retcall=False,
                                 split_dis=self.split_dis,
                                 dis_block_callback=self.disasm_cb)

    def set_options(self, **kwargs):
        "Set options relative to the backend"

        self.options.update(kwargs)

    def clear_jitted_blocks(self):
        "Reset all jitted blocks"
        self.loc_key_to_jit_block.clear()
        self.lbl2bloc.clear()
        self.blocs_mem_interval = interval()

    def add_disassembly_splits(self, *args):
        """The disassembly engine will stop on address in args if they
        are not at the block beginning"""
        self.split_dis.update(set(args))

    def remove_disassembly_splits(self, *args):
        """The disassembly engine will no longer stop on address in args"""
        self.split_dis.difference_update(set(args))

    def load(self):
        "Initialise the Jitter"
        raise NotImplementedError("Abstract class")

    def get_bloc_min_max(self, cur_block):
        "Update cur_block to set min/max address"

        if cur_block.lines:
            cur_block.ad_min = cur_block.lines[0].offset
            cur_block.ad_max = cur_block.lines[-1].offset + cur_block.lines[
                -1].l
        else:
            # 1 byte block for unknown mnemonic
            offset = ir_arch.symbol_pool.loc_key_to_offset(cur_block.loc_key)
            cur_block.ad_min = offset
            cur_block.ad_max = offset + 1

    def add_bloc_to_mem_interval(self, vm, block):
        "Update vm to include block addresses in its memory range"

        self.blocs_mem_interval += interval([(block.ad_min, block.ad_max - 1)])

        vm.reset_code_bloc_pool()
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def jitirblocs(self, label, irblocks):
        """JiT a group of irblocks.
        @label: the label of the irblocks
        @irblocks: a gorup of irblocks
        """

        raise NotImplementedError("Abstract class")

    def add_bloc(self, block):
        """Add a block to JiT and JiT it.
        @block: asm_bloc to add
        """
        irblocks = self.ir_arch.add_block(block, gen_pc_updt=True)
        block.blocks = irblocks
        self.jitirblocs(block.loc_key, irblocks)

    def disbloc(self, addr, vm):
        """Disassemble a new block and JiT it
        @addr: address of the block to disassemble (LocKey or int)
        @vm: VmMngr instance
        """

        # Get the block
        if isinstance(addr, LocKey):
            addr = self.ir_arch.symbol_pool.loc_key_to_offset(addr)
            if addr is None:
                raise RuntimeError("Unknown offset for LocKey")

        # Prepare disassembler
        self.mdis.lines_wd = self.options["jit_maxline"]
        self.mdis.dis_block_callback = self.disasm_cb

        # Disassemble it
        cur_block = self.mdis.dis_block(addr)
        if isinstance(cur_block, AsmBlockBad):
            return cur_block
        # Logging
        if self.log_newbloc:
            print cur_block.to_string(self.mdis.symbol_pool)

        # Update label -> block
        self.lbl2bloc[cur_block.loc_key] = cur_block

        # Store min/max block address needed in jit automod code
        self.get_bloc_min_max(cur_block)

        # JiT it
        self.add_bloc(cur_block)

        # Update jitcode mem range
        self.add_bloc_to_mem_interval(vm, cur_block)
        return cur_block

    def runbloc(self, cpu, lbl, breakpoints):
        """Run the block starting at lbl.
        @cpu: JitCpu instance
        @lbl: target label
        """

        if lbl is None:
            lbl = getattr(cpu, self.ir_arch.pc.name)

        if not lbl in self.loc_key_to_jit_block:
            # Need to JiT the block
            cur_block = self.disbloc(lbl, cpu.vmmngr)
            if isinstance(cur_block, AsmBlockBad):
                errno = cur_block.errno
                if errno == AsmBlockBad.ERROR_IO:
                    cpu.vmmngr.set_exception(EXCEPT_ACCESS_VIOL)
                elif errno == AsmBlockBad.ERROR_CANNOT_DISASM:
                    cpu.set_exception(EXCEPT_UNK_MNEMO)
                else:
                    raise RuntimeError("Unhandled disasm result %r" % errno)
                return lbl

        # Run the block and update cpu/vmmngr state
        return self.exec_wrapper(lbl, cpu, self.loc_key_to_jit_block.data,
                                 breakpoints,
                                 self.options["max_exec_per_call"])

    def blocs2memrange(self, blocks):
        """Return an interval instance standing for blocks addresses
        @blocks: list of asm_bloc instances
        """

        mem_range = interval()

        for block in blocks:
            mem_range += interval([(block.ad_min, block.ad_max - 1)])

        return mem_range

    def __updt_jitcode_mem_range(self, vm):
        """Rebuild the VM blocks address memory range
        @vm: VmMngr instance
        """

        # Reset the current pool
        vm.reset_code_bloc_pool()

        # Add blocks in the pool
        for start, stop in self.blocs_mem_interval:
            vm.add_code_bloc(start, stop + 1)

    def del_bloc_in_range(self, ad1, ad2):
        """Find and remove jitted block in range [ad1, ad2].
        Return the list of block removed.
        @ad1: First address
        @ad2: Last address
        """

        # Find concerned blocks
        modified_blocks = set()
        for block in self.lbl2bloc.values():
            if not block.lines:
                continue
            if block.ad_max <= ad1 or block.ad_min >= ad2:
                # Block not modified
                pass
            else:
                # Modified blocks
                modified_blocks.add(block)

        # Generate interval to delete
        del_interval = self.blocs2memrange(modified_blocks)

        # Remove interval from monitored interval list
        self.blocs_mem_interval -= del_interval

        # Remove modified blocks
        for block in modified_blocks:
            try:
                for irblock in block.blocks:
                    # Remove offset -> jitted block link
                    offset = self.ir_arch.symbol_pool.loc_key_to_offset(
                        irblock.loc_key)
                    if offset in self.loc_key_to_jit_block:
                        del (self.loc_key_to_jit_block[offset])

            except AttributeError:
                # The block has never been translated in IR
                offset = self.ir_arch.symbol_pool.loc_key_to_offset(
                    block.loc_key)
                if offset in self.loc_key_to_jit_block:
                    del (self.loc_key_to_jit_block[offset])

            # Remove label -> block link
            del (self.lbl2bloc[block.loc_key])

        return modified_blocks

    def updt_automod_code_range(self, vm, mem_range):
        """Remove jitted code in range @mem_range
        @vm: VmMngr instance
        @mem_range: list of start/stop addresses
        """
        for addr_start, addr_stop in mem_range:
            self.del_bloc_in_range(addr_start, addr_stop)
        self.__updt_jitcode_mem_range(vm)
        vm.reset_memory_access()

    def updt_automod_code(self, vm):
        """Remove jitted code updated by memory write
        @vm: VmMngr instance
        """
        mem_range = []
        for addr_start, addr_stop in vm.get_memory_write():
            mem_range.append((addr_start, addr_stop))
        self.updt_automod_code_range(vm, mem_range)

    def hash_block(self, block):
        """
        Build a hash of the block @block
        @block: asmblock
        """
        block_raw = "".join(line.b for line in block.lines)
        offset = self.ir_arch.symbol_pool.loc_key_to_offset(block.loc_key)
        block_hash = md5("%X_%s_%s_%s_%s" %
                         (offset, self.arch_name, self.log_mn, self.log_regs,
                          block_raw)).hexdigest()
        return block_hash
Ejemplo n.º 12
0
class JitCore(object):

    "JiT management. This is an abstract class"

    jitted_block_delete_cb = None
    jitted_block_max_size = 10000

    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.addr_mod = interval()

        self.options = {"jit_maxline": 50  # Maximum number of line jitted
                        }

    def set_options(self, **kwargs):
        "Set options relative to the backend"

        self.options.update(kwargs)

    def clear_jitted_blocks(self):
        "Reset all jitted blocks"
        self.lbl2jitbloc.clear()
        self.lbl2bloc.clear()
        self.blocs_mem_interval = interval()

    def add_disassembly_splits(self, *args):
        """The disassembly engine will stop on address in args if they
        are not at the block beginning"""
        self.split_dis.update(set(args))

    def remove_disassembly_splits(self, *args):
        """The disassembly engine will no longer stop on address in args"""
        self.split_dis.difference_update(set(args))

    def load(self, arch, attrib):
        "Initialise the Jitter according to arch and attrib"

        raise NotImplementedError("Abstract class")

    def get_bloc_min_max(self, cur_bloc):
        "Update cur_bloc to set min/max address"

        if cur_bloc.lines:
            cur_bloc.ad_min = cur_bloc.lines[0].offset
            cur_bloc.ad_max = cur_bloc.lines[-1].offset + cur_bloc.lines[-1].l

    def add_bloc_to_mem_interval(self, vm, bloc):
        "Update vm to include bloc addresses in its memory range"

        self.blocs_mem_interval += interval([(bloc.ad_min, bloc.ad_max - 1)])

        vm.reset_code_bloc_pool()
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def jitirblocs(self, label, irblocs):
        """JiT a group of irblocs.
        @label: the label of the irblocs
        @irblocs: a gorup of irblocs
        """

        raise NotImplementedError("Abstract class")

    def add_bloc(self, b):
        """Add a bloc to JiT and JiT it.
        @b: the bloc to add
        """

        irblocs = self.ir_arch.add_bloc(b, gen_pc_updt = True)
        b.irblocs = irblocs
        self.jitirblocs(b.label, irblocs)

    def disbloc(self, addr, cpu, vm):
        "Disassemble a new bloc and JiT it"

        # Get the bloc
        if isinstance(addr, asmbloc.asm_label):
            addr = addr.offset

        label = self.ir_arch.symbol_pool.getby_offset_create(addr)

        # Disassemble it
        try:
            cur_bloc, _ = asmbloc.dis_bloc(self.ir_arch.arch, self.bs, label,
                                           addr, set(),
                                           self.ir_arch.symbol_pool, [],
                                           follow_call=False,
                                           dontdis_retcall=False,
                                           lines_wd=self.options["jit_maxline"],
                                           # max 10 asm lines
                                           attrib=self.ir_arch.attrib,
                                           split_dis=self.split_dis)
        except IOError:
            # vm_exception_flag is set
            cur_bloc = asmbloc.asm_bloc(label)

        # Logging
        if self.log_newbloc:
            print cur_bloc
        if self.disasm_cb is not None:
            self.disasm_cb(cur_bloc)

        # Check for empty blocks
        if not cur_bloc.lines:
            raise ValueError("Cannot JIT a block without any assembly line")

        # Update label -> bloc
        self.lbl2bloc[label] = cur_bloc

        # Store min/max bloc address needed in jit automod code
        self.get_bloc_min_max(cur_bloc)

        # JiT it
        self.add_bloc(cur_bloc)

        # Update jitcode mem range
        self.add_bloc_to_mem_interval(vm, cur_bloc)

    def jit_call(self, label, cpu, _vmmngr, breakpoints):
        """Call the function label with cpu and vmmngr states
        @label: function's label
        @cpu: JitCpu instance
        @breakpoints: Dict instance of used breakpoints
        """
        return self.exec_wrapper(label, cpu, self.lbl2jitbloc.data, breakpoints)

    def runbloc(self, cpu, vm, lbl, breakpoints):
        """Run the bloc starting at lbl.
        @cpu: JitCpu instance
        @vm: VmMngr instance
        @lbl: target label
        """

        if lbl is None:
            lbl = cpu.get_gpreg()[self.ir_arch.pc.name]

        if not lbl in self.lbl2jitbloc:
            # Need to JiT the bloc
            self.disbloc(lbl, cpu, vm)

        # Run the bloc and update cpu/vmmngr state
        ret = self.jit_call(lbl, cpu, vm, breakpoints)

        return ret

    def blocs2memrange(self, blocs):
        """Return an interval instance standing for blocs addresses
        @blocs: list of asm_bloc instances
        """

        mem_range = interval()

        for b in blocs:
            mem_range += interval([(b.ad_min, b.ad_max - 1)])

        return mem_range

    def __updt_jitcode_mem_range(self, vm):
        """Rebuild the VM blocs address memory range
        @vm: VmMngr instance
        """

        # Reset the current pool
        vm.reset_code_bloc_pool()

        # Add blocs in the pool
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def del_bloc_in_range(self, ad1, ad2):
        """Find and remove jitted bloc in range [ad1, ad2].
        Return the list of bloc removed.
        @ad1: First address
        @ad2: Last address
        """

        # Find concerned blocs
        modified_blocs = set()
        for b in self.lbl2bloc.values():
            if not b.lines:
                continue
            if b.ad_max <= ad1 or b.ad_min >= ad2:
                # Bloc not modified
                pass
            else:
                # Modified blocs
                modified_blocs.add(b)

        # Generate interval to delete
        del_interval = self.blocs2memrange(modified_blocs)

        # Remove interval from monitored interval list
        self.blocs_mem_interval -= del_interval

        # Remove modified blocs
        for b in modified_blocs:
            try:
                for irbloc in b.irblocs:

                    # Remove offset -> jitted bloc link
                    if irbloc.label.offset in self.lbl2jitbloc:
                        del(self.lbl2jitbloc[irbloc.label.offset])

            except AttributeError:
                # The bloc has never been translated in IR
                if b.label.offset in self.lbl2jitbloc:
                    del(self.lbl2jitbloc[b.label.offset])

            # Remove label -> bloc link
            del(self.lbl2bloc[b.label])

        return modified_blocs

    def updt_automod_code(self, vm):
        """Remove code jitted in range self.addr_mod
        @vm: VmMngr instance
        """
        for addr_start, addr_stop in self.addr_mod:
            self.del_bloc_in_range(addr_start, addr_stop + 1)
        self.__updt_jitcode_mem_range(vm)
        self.addr_mod = interval()

    def automod_cb(self, addr=0, size=0):
        self.addr_mod += interval([(addr, addr + size / 8 - 1)])
        return None
Ejemplo n.º 13
0
class JitCore(object):

    "JiT management. This is an abstract class"

    jitted_block_delete_cb = None
    jitted_block_max_size = 10000

    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.options = {
            "jit_maxline": 50,  # Maximum number of line jitted
            "max_exec_per_call": 0  # 0 means no limit
        }

        self.mdis = asmbloc.disasmEngine(ir_arch.arch,
                                         ir_arch.attrib,
                                         bs,
                                         lines_wd=self.options["jit_maxline"],
                                         symbol_pool=ir_arch.symbol_pool,
                                         follow_call=False,
                                         dontdis_retcall=False,
                                         split_dis=self.split_dis,
                                         dis_bloc_callback=self.disasm_cb)

    def set_options(self, **kwargs):
        "Set options relative to the backend"

        self.options.update(kwargs)

    def clear_jitted_blocks(self):
        "Reset all jitted blocks"
        self.lbl2jitbloc.clear()
        self.lbl2bloc.clear()
        self.blocs_mem_interval = interval()

    def add_disassembly_splits(self, *args):
        """The disassembly engine will stop on address in args if they
        are not at the block beginning"""
        self.split_dis.update(set(args))

    def remove_disassembly_splits(self, *args):
        """The disassembly engine will no longer stop on address in args"""
        self.split_dis.difference_update(set(args))

    def load(self):
        "Initialise the Jitter"
        raise NotImplementedError("Abstract class")

    def get_bloc_min_max(self, cur_bloc):
        "Update cur_bloc to set min/max address"

        if cur_bloc.lines:
            cur_bloc.ad_min = cur_bloc.lines[0].offset
            cur_bloc.ad_max = cur_bloc.lines[-1].offset + cur_bloc.lines[-1].l
        else:
            # 1 byte block for unknown mnemonic
            cur_bloc.ad_min = cur_bloc.label.offset
            cur_bloc.ad_max = cur_bloc.label.offset + 1

    def add_bloc_to_mem_interval(self, vm, bloc):
        "Update vm to include bloc addresses in its memory range"

        self.blocs_mem_interval += interval([(bloc.ad_min, bloc.ad_max - 1)])

        vm.reset_code_bloc_pool()
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def jitirblocs(self, label, irblocs):
        """JiT a group of irblocs.
        @label: the label of the irblocs
        @irblocs: a gorup of irblocs
        """

        raise NotImplementedError("Abstract class")

    def add_bloc(self, b):
        """Add a bloc to JiT and JiT it.
        @b: the bloc to add
        """

        irblocs = self.ir_arch.add_bloc(b, gen_pc_updt=True)
        b.irblocs = irblocs
        self.jitirblocs(b.label, irblocs)

    def disbloc(self, addr, vm):
        """Disassemble a new bloc and JiT it
        @addr: address of the block to disassemble (asm_label or int)
        @vm: VmMngr instance
        """

        # Get the bloc
        if isinstance(addr, asmbloc.asm_label):
            addr = addr.offset

        # Prepare disassembler
        self.mdis.job_done.clear()
        self.mdis.lines_wd = self.options["jit_maxline"]
        self.mdis.dis_bloc_callback = self.disasm_cb

        # Disassemble it
        try:
            cur_bloc = self.mdis.dis_bloc(addr)
        except IOError:
            # vm_exception_flag is set
            label = self.ir_arch.symbol_pool.getby_offset_create(addr)
            cur_bloc = asmbloc.asm_block_bad(label)

        # Logging
        if self.log_newbloc:
            print cur_bloc

        # Update label -> bloc
        self.lbl2bloc[cur_bloc.label] = cur_bloc

        # Store min/max bloc address needed in jit automod code
        self.get_bloc_min_max(cur_bloc)

        # JiT it
        self.add_bloc(cur_bloc)

        # Update jitcode mem range
        self.add_bloc_to_mem_interval(vm, cur_bloc)

    def runbloc(self, cpu, lbl, breakpoints):
        """Run the bloc starting at lbl.
        @cpu: JitCpu instance
        @lbl: target label
        """

        if lbl is None:
            lbl = getattr(cpu, self.ir_arch.pc.name)

        if not lbl in self.lbl2jitbloc:
            # Need to JiT the bloc
            self.disbloc(lbl, cpu.vmmngr)

        # Run the bloc and update cpu/vmmngr state
        return self.exec_wrapper(lbl, cpu, self.lbl2jitbloc.data, breakpoints,
                                 self.options["max_exec_per_call"])

    def blocs2memrange(self, blocs):
        """Return an interval instance standing for blocs addresses
        @blocs: list of asm_bloc instances
        """

        mem_range = interval()

        for b in blocs:
            mem_range += interval([(b.ad_min, b.ad_max - 1)])

        return mem_range

    def __updt_jitcode_mem_range(self, vm):
        """Rebuild the VM blocs address memory range
        @vm: VmMngr instance
        """

        # Reset the current pool
        vm.reset_code_bloc_pool()

        # Add blocs in the pool
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def del_bloc_in_range(self, ad1, ad2):
        """Find and remove jitted bloc in range [ad1, ad2].
        Return the list of bloc removed.
        @ad1: First address
        @ad2: Last address
        """

        # Find concerned blocs
        modified_blocs = set()
        for b in self.lbl2bloc.values():
            if not b.lines:
                continue
            if b.ad_max <= ad1 or b.ad_min >= ad2:
                # Bloc not modified
                pass
            else:
                # Modified blocs
                modified_blocs.add(b)

        # Generate interval to delete
        del_interval = self.blocs2memrange(modified_blocs)

        # Remove interval from monitored interval list
        self.blocs_mem_interval -= del_interval

        # Remove modified blocs
        for b in modified_blocs:
            try:
                for irbloc in b.irblocs:

                    # Remove offset -> jitted bloc link
                    if irbloc.label.offset in self.lbl2jitbloc:
                        del (self.lbl2jitbloc[irbloc.label.offset])

            except AttributeError:
                # The bloc has never been translated in IR
                if b.label.offset in self.lbl2jitbloc:
                    del (self.lbl2jitbloc[b.label.offset])

            # Remove label -> bloc link
            del (self.lbl2bloc[b.label])

        return modified_blocs

    def updt_automod_code_range(self, vm, mem_range):
        """Remove jitted code in range @mem_range
        @vm: VmMngr instance
        @mem_range: list of start/stop addresses
        """
        for addr_start, addr_stop in mem_range:
            self.del_bloc_in_range(addr_start, addr_stop)
        self.__updt_jitcode_mem_range(vm)
        vm.reset_memory_access()

    def updt_automod_code(self, vm):
        """Remove jitted code updated by memory write
        @vm: VmMngr instance
        """
        mem_range = []
        for addr_start, addr_stop in vm.get_memory_write():
            mem_range.append((addr_start, addr_stop))
        self.updt_automod_code_range(vm, mem_range)
Ejemplo n.º 14
0
class JitCore(object):

    "JiT management. This is an abstract class"

    jitted_block_delete_cb = None
    jitted_block_max_size = 10000

    def __init__(self, ir_arch, bs=None):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bs: bitstream
        """

        self.ir_arch = ir_arch
        self.bs = bs
        self.known_blocs = {}
        self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size,
                                       delete_cb=self.jitted_block_delete_cb)
        self.lbl2bloc = {}
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.segm_to_do = set()
        self.job_done = set()
        self.jitcount = 0
        self.addr2obj = {}
        self.addr2objref = {}
        self.blocs_mem_interval = interval()
        self.disasm_cb = None
        self.split_dis = set()
        self.addr_mod = interval()

        self.options = {
            "jit_maxline": 50  # Maximum number of line jitted
        }

    def set_options(self, **kwargs):
        "Set options relative to the backend"

        self.options.update(kwargs)

    def clear_jitted_blocks(self):
        "Reset all jitted blocks"
        self.lbl2jitbloc.clear()
        self.lbl2bloc.clear()
        self.blocs_mem_interval = interval()

    def add_disassembly_splits(self, *args):
        """The disassembly engine will stop on address in args if they
        are not at the block beginning"""
        self.split_dis.update(set(args))

    def remove_disassembly_splits(self, *args):
        """The disassembly engine will no longer stop on address in args"""
        self.split_dis.difference_update(set(args))

    def load(self, arch, attrib):
        "Initialise the Jitter according to arch and attrib"

        raise NotImplementedError("Abstract class")

    def get_bloc_min_max(self, cur_bloc):
        "Update cur_bloc to set min/max address"

        if cur_bloc.lines:
            cur_bloc.ad_min = cur_bloc.lines[0].offset
            cur_bloc.ad_max = cur_bloc.lines[-1].offset + cur_bloc.lines[-1].l

    def add_bloc_to_mem_interval(self, vm, bloc):
        "Update vm to include bloc addresses in its memory range"

        self.blocs_mem_interval += interval([(bloc.ad_min, bloc.ad_max - 1)])

        vm.reset_code_bloc_pool()
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def jitirblocs(self, label, irblocs):
        """JiT a group of irblocs.
        @label: the label of the irblocs
        @irblocs: a gorup of irblocs
        """

        raise NotImplementedError("Abstract class")

    def add_bloc(self, b):
        """Add a bloc to JiT and JiT it.
        @b: the bloc to add
        """

        irblocs = self.ir_arch.add_bloc(b, gen_pc_updt=True)
        b.irblocs = irblocs
        self.jitirblocs(b.label, irblocs)

    def disbloc(self, addr, cpu, vm):
        "Disassemble a new bloc and JiT it"

        # Get the bloc
        if isinstance(addr, asmbloc.asm_label):
            addr = addr.offset

        label = self.ir_arch.symbol_pool.getby_offset_create(addr)

        # Disassemble it
        try:
            cur_bloc, _ = asmbloc.dis_bloc(
                self.ir_arch.arch,
                self.bs,
                label,
                addr,
                set(),
                self.ir_arch.symbol_pool,
                [],
                follow_call=False,
                dontdis_retcall=False,
                lines_wd=self.options["jit_maxline"],
                # max 10 asm lines
                attrib=self.ir_arch.attrib,
                split_dis=self.split_dis)
        except IOError:
            # vm_exception_flag is set
            cur_bloc = asmbloc.asm_bloc(label)

        # Logging
        if self.log_newbloc:
            print cur_bloc
        if self.disasm_cb is not None:
            self.disasm_cb(cur_bloc)

        # Check for empty blocks
        if not cur_bloc.lines:
            raise ValueError("Cannot JIT a block without any assembly line")

        # Update label -> bloc
        self.lbl2bloc[label] = cur_bloc

        # Store min/max bloc address needed in jit automod code
        self.get_bloc_min_max(cur_bloc)

        # JiT it
        self.add_bloc(cur_bloc)

        # Update jitcode mem range
        self.add_bloc_to_mem_interval(vm, cur_bloc)

    def jit_call(self, label, cpu, _vmmngr, breakpoints):
        """Call the function label with cpu and vmmngr states
        @label: function's label
        @cpu: JitCpu instance
        @breakpoints: Dict instance of used breakpoints
        """
        return self.exec_wrapper(label, cpu, self.lbl2jitbloc.data,
                                 breakpoints)

    def runbloc(self, cpu, vm, lbl, breakpoints):
        """Run the bloc starting at lbl.
        @cpu: JitCpu instance
        @vm: VmMngr instance
        @lbl: target label
        """

        if lbl is None:
            lbl = cpu.get_gpreg()[self.ir_arch.pc.name]

        if not lbl in self.lbl2jitbloc:
            # Need to JiT the bloc
            self.disbloc(lbl, cpu, vm)

        # Run the bloc and update cpu/vmmngr state
        ret = self.jit_call(lbl, cpu, vm, breakpoints)

        return ret

    def blocs2memrange(self, blocs):
        """Return an interval instance standing for blocs addresses
        @blocs: list of asm_bloc instances
        """

        mem_range = interval()

        for b in blocs:
            mem_range += interval([(b.ad_min, b.ad_max - 1)])

        return mem_range

    def __updt_jitcode_mem_range(self, vm):
        """Rebuild the VM blocs address memory range
        @vm: VmMngr instance
        """

        # Reset the current pool
        vm.reset_code_bloc_pool()

        # Add blocs in the pool
        for a, b in self.blocs_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def del_bloc_in_range(self, ad1, ad2):
        """Find and remove jitted bloc in range [ad1, ad2].
        Return the list of bloc removed.
        @ad1: First address
        @ad2: Last address
        """

        # Find concerned blocs
        modified_blocs = set()
        for b in self.lbl2bloc.values():
            if not b.lines:
                continue
            if b.ad_max <= ad1 or b.ad_min >= ad2:
                # Bloc not modified
                pass
            else:
                # Modified blocs
                modified_blocs.add(b)

        # Generate interval to delete
        del_interval = self.blocs2memrange(modified_blocs)

        # Remove interval from monitored interval list
        self.blocs_mem_interval -= del_interval

        # Remove modified blocs
        for b in modified_blocs:
            try:
                for irbloc in b.irblocs:

                    # Remove offset -> jitted bloc link
                    if irbloc.label.offset in self.lbl2jitbloc:
                        del (self.lbl2jitbloc[irbloc.label.offset])

            except AttributeError:
                # The bloc has never been translated in IR
                if b.label.offset in self.lbl2jitbloc:
                    del (self.lbl2jitbloc[b.label.offset])

            # Remove label -> bloc link
            del (self.lbl2bloc[b.label])

        return modified_blocs

    def updt_automod_code(self, vm):
        """Remove code jitted in range self.addr_mod
        @vm: VmMngr instance
        """
        for addr_start, addr_stop in self.addr_mod:
            self.del_bloc_in_range(addr_start, addr_stop + 1)
        self.__updt_jitcode_mem_range(vm)
        self.addr_mod = interval()

    def automod_cb(self, addr=0, size=0):
        self.addr_mod += interval([(addr, addr + size / 8 - 1)])
        return None
Ejemplo n.º 15
0
class JitCore(object):

    "JiT management. This is an abstract class"

    # Jitted function's name
    FUNCNAME = "block_entry"

    jitted_block_delete_cb = None
    jitted_block_max_size = 10000

    def __init__(self, ir_arch, bin_stream):
        """Initialise a JitCore instance.
        @ir_arch: ir instance for current architecture
        @bin_stream: bin_stream instance
        """
        # Arch related
        self.ir_arch = ir_arch
        self.ircfg = self.ir_arch.new_ircfg()
        self.arch_name = "%s%s" % (self.ir_arch.arch.name, self.ir_arch.attrib)

        # Structures for block tracking
        self.offset_to_jitted_func = BoundedDict(
            self.jitted_block_max_size, delete_cb=self.jitted_block_delete_cb)
        self.loc_key_to_block = {}
        self.blocks_mem_interval = interval()

        # Logging & options
        self.log_mn = False
        self.log_regs = False
        self.log_newbloc = False
        self.options = {
            "jit_maxline": 50,  # Maximum number of line jitted
            "max_exec_per_call": 0  # 0 means no limit
        }

        # Disassembly Engine
        self.split_dis = set()
        self.mdis = disasmEngine(
            ir_arch.arch,
            ir_arch.attrib,
            bin_stream,
            lines_wd=self.options["jit_maxline"],
            loc_db=ir_arch.loc_db,
            follow_call=False,
            dontdis_retcall=False,
            split_dis=self.split_dis,
        )

    def set_options(self, **kwargs):
        "Set options relative to the backend"
        self.options.update(kwargs)

    def clear_jitted_blocks(self):
        "Reset all jitted blocks"
        self.offset_to_jitted_func.clear()
        self.loc_key_to_block.clear()
        self.blocks_mem_interval = interval()

    def add_disassembly_splits(self, *args):
        """The disassembly engine will stop on address in args if they
        are not at the block beginning"""
        self.split_dis.update(set(args))

    def remove_disassembly_splits(self, *args):
        """The disassembly engine will no longer stop on address in args"""
        self.split_dis.difference_update(set(args))

    def load(self):
        "Initialise the Jitter"
        raise NotImplementedError("Abstract class")

    def set_block_min_max(self, cur_block):
        "Update cur_block to set min/max address"

        if cur_block.lines:
            cur_block.ad_min = cur_block.lines[0].offset
            cur_block.ad_max = cur_block.lines[-1].offset + cur_block.lines[
                -1].l
        else:
            # 1 byte block for unknown mnemonic
            offset = ir_arch.loc_db.get_location_offset(cur_block.loc_key)
            cur_block.ad_min = offset
            cur_block.ad_max = offset + 1

    def add_block_to_mem_interval(self, vm, block):
        "Update vm to include block addresses in its memory range"
        self.blocks_mem_interval += interval([(block.ad_min, block.ad_max - 1)
                                              ])

        vm.reset_code_bloc_pool()
        for a, b in self.blocks_mem_interval:
            vm.add_code_bloc(a, b + 1)

    def add_block(self, block):
        """Add a block to JiT and JiT it.
        @block: asm_bloc to add
        """
        raise NotImplementedError("Abstract class")

    def disasm_and_jit_block(self, addr, vm):
        """Disassemble a new block and JiT it
        @addr: address of the block to disassemble (LocKey or int)
        @vm: VmMngr instance
        """

        # Get the block
        if isinstance(addr, LocKey):
            addr = self.ir_arch.loc_db.get_location_offset(addr)
            if addr is None:
                raise RuntimeError("Unknown offset for LocKey")

        # Prepare disassembler
        self.mdis.lines_wd = self.options["jit_maxline"]

        # Disassemble it
        cur_block = self.mdis.dis_block(addr)
        if isinstance(cur_block, AsmBlockBad):
            return cur_block
        # Logging
        if self.log_newbloc:
            print cur_block.to_string(self.mdis.loc_db)

        # Update label -> block
        self.loc_key_to_block[cur_block.loc_key] = cur_block

        # Store min/max block address needed in jit automod code
        self.set_block_min_max(cur_block)

        # JiT it
        self.add_block(cur_block)

        # Update jitcode mem range
        self.add_block_to_mem_interval(vm, cur_block)
        return cur_block

    def run_at(self, cpu, offset, stop_offsets):
        """Run from the starting address @offset.
        Execution will stop if:
        - max_exec_per_call option is reached
        - a new, yet unknown, block is reached after the execution of block at
          address @offset
        - an address in @stop_offsets is reached
        @cpu: JitCpu instance
        @offset: starting address (int)
        @stop_offsets: set of address on which the jitter must stop
        """

        if offset is None:
            offset = getattr(cpu, self.ir_arch.pc.name)

        if offset not in self.offset_to_jitted_func:
            # Need to JiT the block
            cur_block = self.disasm_and_jit_block(offset, cpu.vmmngr)
            if isinstance(cur_block, AsmBlockBad):
                errno = cur_block.errno
                if errno == AsmBlockBad.ERROR_IO:
                    cpu.vmmngr.set_exception(EXCEPT_ACCESS_VIOL)
                elif errno == AsmBlockBad.ERROR_CANNOT_DISASM:
                    cpu.set_exception(EXCEPT_UNK_MNEMO)
                else:
                    raise RuntimeError("Unhandled disasm result %r" % errno)
                return offset

        # Run the block and update cpu/vmmngr state
        return self.exec_wrapper(offset, cpu, self.offset_to_jitted_func.data,
                                 stop_offsets,
                                 self.options["max_exec_per_call"])

    def blocks_to_memrange(self, blocks):
        """Return an interval instance standing for blocks addresses
        @blocks: list of AsmBlock instances
        """

        mem_range = interval()

        for block in blocks:
            mem_range += interval([(block.ad_min, block.ad_max - 1)])

        return mem_range

    def __updt_jitcode_mem_range(self, vm):
        """Rebuild the VM blocks address memory range
        @vm: VmMngr instance
        """

        # Reset the current pool
        vm.reset_code_bloc_pool()

        # Add blocks in the pool
        for start, stop in self.blocks_mem_interval:
            vm.add_code_bloc(start, stop + 1)

    def del_block_in_range(self, ad1, ad2):
        """Find and remove jitted block in range [ad1, ad2].
        Return the list of block removed.
        @ad1: First address
        @ad2: Last address
        """

        # Find concerned blocks
        modified_blocks = set()
        for block in self.loc_key_to_block.values():
            if not block.lines:
                continue
            if block.ad_max <= ad1 or block.ad_min >= ad2:
                # Block not modified
                pass
            else:
                # Modified blocks
                modified_blocks.add(block)

        # Generate interval to delete
        del_interval = self.blocks_to_memrange(modified_blocks)

        # Remove interval from monitored interval list
        self.blocks_mem_interval -= del_interval

        # Remove modified blocks
        for block in modified_blocks:
            try:
                for irblock in block.blocks:
                    # Remove offset -> jitted block link
                    offset = self.ir_arch.loc_db.get_location_offset(
                        irblock.loc_key)
                    if offset in self.offset_to_jitted_func:
                        del (self.offset_to_jitted_func[offset])

            except AttributeError:
                # The block has never been translated in IR
                offset = self.ir_arch.loc_db.get_location_offset(block.loc_key)
                if offset in self.offset_to_jitted_func:
                    del (self.offset_to_jitted_func[offset])

            # Remove label -> block link
            del (self.loc_key_to_block[block.loc_key])

        return modified_blocks

    def updt_automod_code_range(self, vm, mem_range):
        """Remove jitted code in range @mem_range
        @vm: VmMngr instance
        @mem_range: list of start/stop addresses
        """
        for addr_start, addr_stop in mem_range:
            self.del_block_in_range(addr_start, addr_stop)
        self.__updt_jitcode_mem_range(vm)
        vm.reset_memory_access()

    def updt_automod_code(self, vm):
        """Remove jitted code updated by memory write
        @vm: VmMngr instance
        """
        mem_range = []
        for addr_start, addr_stop in vm.get_memory_write():
            mem_range.append((addr_start, addr_stop))
        self.updt_automod_code_range(vm, mem_range)

    def hash_block(self, block):
        """
        Build a hash of the block @block
        @block: asmblock
        """
        block_raw = "".join(line.b for line in block.lines)
        offset = self.ir_arch.loc_db.get_location_offset(block.loc_key)
        block_hash = md5("%X_%s_%s_%s_%s" %
                         (offset, self.arch_name, self.log_mn, self.log_regs,
                          block_raw)).hexdigest()
        return block_hash

    @property
    def disasm_cb(self):
        warnings.warn("Deprecated API: use .mdis.dis_block_callback")
        return self.mdis.dis_block_callback

    @disasm_cb.setter
    def disasm_cb(self, value):
        warnings.warn("Deprecated API: use .mdis.dis_block_callback")
        self.mdis.dis_block_callback = value