From 2d3394d8b84fe018aade8b079fa12f68c7371d5d Mon Sep 17 00:00:00 2001 From: xor Date: Wed, 8 May 2024 23:35:17 +0200 Subject: [PATCH] feat: Add support for x86 16bit platform --- angr_platforms/X86_16/__init__.py | 1 + angr_platforms/X86_16/access.py | 169 +++++ angr_platforms/X86_16/arch_86_16.py | 257 +++++++ angr_platforms/X86_16/cr.py | 33 + angr_platforms/X86_16/debug.py | 63 ++ angr_platforms/X86_16/dev_io.py | 59 ++ angr_platforms/X86_16/eflags.py | 281 ++++++++ angr_platforms/X86_16/emu.py | 60 ++ angr_platforms/X86_16/emulator.py | 8 + angr_platforms/X86_16/exception.py | 34 + angr_platforms/X86_16/exec.py | 208 ++++++ angr_platforms/X86_16/hardware.py | 11 + angr_platforms/X86_16/instr16.py | 992 +++++++++++++++++++++++++++ angr_platforms/X86_16/instr32.py | 866 +++++++++++++++++++++++ angr_platforms/X86_16/instr_base.py | 714 +++++++++++++++++++ angr_platforms/X86_16/instruction.py | 100 +++ angr_platforms/X86_16/interrupt.py | 50 ++ angr_platforms/X86_16/io.py | 90 +++ angr_platforms/X86_16/lift_86_16.py | 108 +++ angr_platforms/X86_16/memory.py | 81 +++ angr_platforms/X86_16/parse.py | 147 ++++ angr_platforms/X86_16/processor.py | 291 ++++++++ angr_platforms/X86_16/regs.py | 57 ++ angr_platforms/X86_16/simos_86_16.py | 42 ++ tests/test_x86_16bit.py | 298 ++++++++ 25 files changed, 5020 insertions(+) create mode 100644 angr_platforms/X86_16/__init__.py create mode 100644 angr_platforms/X86_16/access.py create mode 100644 angr_platforms/X86_16/arch_86_16.py create mode 100644 angr_platforms/X86_16/cr.py create mode 100644 angr_platforms/X86_16/debug.py create mode 100644 angr_platforms/X86_16/dev_io.py create mode 100644 angr_platforms/X86_16/eflags.py create mode 100644 angr_platforms/X86_16/emu.py create mode 100644 angr_platforms/X86_16/emulator.py create mode 100644 angr_platforms/X86_16/exception.py create mode 100644 angr_platforms/X86_16/exec.py create mode 100644 angr_platforms/X86_16/hardware.py create mode 100644 angr_platforms/X86_16/instr16.py create mode 100644 angr_platforms/X86_16/instr32.py create mode 100644 angr_platforms/X86_16/instr_base.py create mode 100644 angr_platforms/X86_16/instruction.py create mode 100644 angr_platforms/X86_16/interrupt.py create mode 100644 angr_platforms/X86_16/io.py create mode 100644 angr_platforms/X86_16/lift_86_16.py create mode 100644 angr_platforms/X86_16/memory.py create mode 100644 angr_platforms/X86_16/parse.py create mode 100644 angr_platforms/X86_16/processor.py create mode 100644 angr_platforms/X86_16/regs.py create mode 100644 angr_platforms/X86_16/simos_86_16.py create mode 100644 tests/test_x86_16bit.py diff --git a/angr_platforms/X86_16/__init__.py b/angr_platforms/X86_16/__init__.py new file mode 100644 index 0000000..c6da9fc --- /dev/null +++ b/angr_platforms/X86_16/__init__.py @@ -0,0 +1 @@ +__all__ = ["arch_86_16", "lift_86_16", "simos_86_16"] diff --git a/angr_platforms/X86_16/access.py b/angr_platforms/X86_16/access.py new file mode 100644 index 0000000..cc5f5e0 --- /dev/null +++ b/angr_platforms/X86_16/access.py @@ -0,0 +1,169 @@ +from pyvex.lifting.util.vex_helper import JumpKind, Type + +from .hardware import Hardware +from .regs import reg16_t, reg32_t, sgreg_t + +# Constants for access modes +MODE_READ = 0 +MODE_WRITE = 1 +MODE_EXEC = 2 + + +class DataAccess(Hardware): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.tlb = [] # Translation Lookaside Buffer + + def set_segment(self, reg, sel): + self.set_gpreg(reg, sel) + + def get_segment(self, reg): + return self.lifter_instruction.get(reg.name.lower(), Type.int_16) + + def trans_v2p(self, mode, seg, vaddr): + laddr = self.trans_v2l(mode, seg, vaddr) + + paddr = laddr + return paddr + + def trans_v2l(self, mode, seg, vaddr): + if isinstance(seg, sgreg_t) and seg == sgreg_t.SS: + laddr = vaddr.cast_to(Type.int_16) # Simplify ss: for decompiler + else: + if isinstance(seg, sgreg_t): + sg = self.get_sgreg(seg) + elif isinstance(seg, int): + sg = self.constant(seg, Type.int_16) + else: + sg = seg + if not isinstance(vaddr, int): + vaddr = vaddr.cast_to(Type.int_32) + laddr = (sg.cast_to(Type.int_32) << 4) + vaddr + return laddr + + def search_tlb(self, vpn): + if vpn + 1 > len(self.tlb) or self.tlb[vpn] is None: + return None + return self.tlb[vpn] + + def cache_tlb(self, vpn, pte): + if vpn + 1 > len(self.tlb): + self.tlb.extend([None] * (vpn + 1 - len(self.tlb))) + self.tlb[vpn] = pte + + def push32(self, value): + self.update_gpreg(reg32_t.ESP, -4) + sp = self.get_gpreg(reg32_t.ESP) + self.write_mem32_seg(sgreg_t.SS, sp, value) + + def pop32(self): + sp = self.get_gpreg(reg32_t.ESP) + value = self.read_mem32_seg(sgreg_t.SS, sp) + self.update_gpreg(reg32_t.ESP, 4) + return value + + def push16(self, value): + self.update_gpreg(reg16_t.SP, -2) + sp = self.get_gpreg(reg16_t.SP) + self.write_mem16_seg(sgreg_t.SS, sp, value) + + def pop16(self): + sp = self.get_gpreg(reg16_t.SP) + value = self.read_mem16_seg(sgreg_t.SS, sp) + self.update_gpreg(reg16_t.SP, 2) + return value + + def read_mem32_seg(self, seg, addr): + paddr = self.trans_v2p(MODE_READ, seg, addr) + return self.read_mem32(paddr) + io_base = self.chk_memio(paddr) + return ( + self.read_memio32(io_base, paddr - io_base) + if io_base + else self.read_mem32(paddr) + ) + + def read_mem16_seg(self, seg, addr): + paddr = self.trans_v2p(MODE_READ, seg, addr) + return self.read_mem16(paddr) + io_base = self.chk_memio(paddr) + return ( + self.read_memio16(io_base, paddr - io_base) + if io_base + else self.read_mem16(paddr) + ) + + def read_mem8_seg(self, seg, addr): + paddr = self.trans_v2p(MODE_READ, seg, addr) + return self.read_mem8(paddr) + io_base = self.chk_memio(paddr) + return ( + self.read_memio8(io_base, paddr - io_base) + if io_base + else self.read_mem8(paddr) + ) + + def write_mem32_seg(self, seg, addr, value): + paddr = self.trans_v2p(MODE_WRITE, seg, addr) + self.write_mem32(paddr, value) + return + io_base = self.chk_memio(paddr) + if io_base: + self.write_memio32(io_base, paddr - io_base, value) + else: + self.write_mem32(paddr, value) + + def write_mem16_seg(self, seg, addr, value): + paddr = self.trans_v2p(MODE_WRITE, seg, addr) + self.write_mem16(paddr, value) + return + io_base = self.chk_memio(paddr) + if io_base: + self.write_memio16(io_base, paddr - io_base, value) + else: + self.write_mem16(paddr, value) + + def write_mem8_seg(self, seg, addr, value): + paddr = self.trans_v2p(MODE_WRITE, seg, addr) + self.write_mem8(paddr, value) + return + io_base = self.chk_memio(paddr) + if io_base: + self.write_memio8(io_base, paddr - io_base, value) + else: + self.write_mem8(paddr, value) + + def get_code8(self, offset): + assert offset == 0 + return self.bitstream.read("uint:8") + + def get_code16(self, offset): + assert offset == 0 + return self.bitstream.read("uintle:16") + + def get_code32(self, offset): + assert offset == 0 + return self.bitstream.read("uintle:32") + + def get_data16(self, seg, addr): + return self.read_mem16_seg(seg, addr) + + def get_data32(self, seg, addr): + return self.read_mem32_seg(seg, addr) + + def get_data8(self, seg, addr): + return self.read_mem8_seg(seg, addr) + + def put_data8(self, seg, addr, value): + self.write_mem8_seg(seg, addr, value) + + def put_data16(self, seg, addr, value): + self.write_mem16_seg(seg, addr, value) + + def put_data32(self, seg, addr, value): + self.write_mem32_seg(seg, addr, value) + + def callf(self, seg, ip): + self.push16(self.get_sgreg(sgreg_t.CS)) + self.push16(self.get_gpreg(reg16_t.IP) + 5) + self.lifter_instruction.jump(None, self.trans_v2p(0, seg, ip), jumpkind=JumpKind.Call) diff --git a/angr_platforms/X86_16/arch_86_16.py b/angr_platforms/X86_16/arch_86_16.py new file mode 100644 index 0000000..c6eaca6 --- /dev/null +++ b/angr_platforms/X86_16/arch_86_16.py @@ -0,0 +1,257 @@ +from archinfo import ArchError, RegisterOffset + +try: + import capstone as _capstone +except ImportError: + _capstone = None + +try: + import keystone as _keystone +except ImportError: + _keystone = None + +try: + import unicorn as _unicorn +except ImportError: + _unicorn = None + +from archinfo.arch import Arch, Endness, Register, register_arch + + +class Arch86_16(Arch): + + def __init__(self, endness=Endness.LE): + offset = 0 + for reg in self.register_list: + reg.vex_offset = offset + offset += reg.size + + super().__init__(endness) + self.reg_blacklist = [] + self.reg_blacklist_offsets = [] + self.vex_archinfo = None + self.vex_cc_regs = None + self.vex_to_unicorn_map = None + #self.registers = self.register_list + + name = "86_16" + bits = 16 + stack_change = -2 + vex_arch = None + vex_support = False + vex_conditional_helpers = False + sizeof = {"short": 16, "int": 16, "long": 32, "long long": 32} + ld_linux_name = None + linux_name = None + lib_paths = [] + #max_inst_bytes = 4 + #ip_offset = 0x80000000 + #sp_offset = 16 + call_pushes_ret = True + instruction_endness = Endness.LE + # FIXME: something in angr assumes that sizeof(long) == sizeof(return address on stack) + #initial_sp = 0x7fff + call_sp_fix = 2 + instruction_alignment = 1 + #ioreg_offset = 0x20 + memory_endness = Endness.LE + register_endness = Endness.LE + + + elf_tls = None + if _capstone: + cs_arch = _capstone.CS_ARCH_X86 # Disassembler + cs_mode = _capstone.CS_MODE_16 + _capstone.CS_MODE_LITTLE_ENDIAN + _cs_x86_syntax = None # Set it to 'att' in order to use AT&T syntax for x86 + if _keystone: + ks_arch = _keystone.KS_ARCH_X86 # Assembler + ks_mode = _keystone.KS_MODE_16 + _keystone.KS_MODE_LITTLE_ENDIAN + _ks_x86_syntax = None + uc_arch = _unicorn.UC_ARCH_X86 if _unicorn else None # Emulator + uc_mode = (_unicorn.UC_MODE_16 + _unicorn.UC_MODE_LITTLE_ENDIAN) if _unicorn else None + uc_const = _unicorn.x86_const if _unicorn else None + uc_prefix = "UC_X86_" if _unicorn else None + function_prologs = {rb"\x55\x8b\xec", # push ebp; mov ebp, esp + rb"\x55\x89\xe5"} # push ebp; mov ebp, esp + function_epilogs = { + rb"\xc9\xc3", # leave; ret + rb"([^\x41][\x50-\x5f]{1}|\x41[\x50-\x5f])\xc3"} # pop ; ret + ret_offset = RegisterOffset(0) # ax - syscall return register? + ret_instruction = b"\xc3" + nop_instruction = b"\x90" + + + register_list = [ + Register( + name="eax", + size=4, + subregisters=[("ax", 0, 2), ("al", 0, 1), ("ah", 1, 1)], + general_purpose=True, + argument=True, + linux_entry_value=0x1C, + ), + Register( + name="ecx", + size=4, + subregisters=[("cx", 0, 2), ("cl", 0, 1), ("ch", 1, 1)], + general_purpose=True, + ), + Register( + name="edx", + size=4, + subregisters=[("dx", 0, 2), ("dl", 0, 1), ("dh", 1, 1)], + general_purpose=True, + ), + Register( + name="ebx", + size=4, + subregisters=[("bx", 0, 2), ("bl", 0, 1), ("bh", 1, 1)], + general_purpose=True, + ), + Register( + name="esp", + size=4, + subregisters=[("sp", 0, 2)], + alias_names=("stack_base",), + #alias_names=("sp",), + general_purpose=True, + default_value=(Arch.initial_sp, True, "global"), + ), + Register(name="ebp", size=4, subregisters=[("bp", 0, 2)], general_purpose=True, argument=True, + ), + Register( + name="esi", + size=4, + subregisters=[("si", 0, 2), ("sil", 0, 1), ("sih", 1, 1)], + general_purpose=True, + ), + Register( + name="edi", + size=4, + subregisters=[("di", 0, 2), ("dil", 0, 1), ("dih", 1, 1)], + general_purpose=True, + ), + # Register(name="cc_op", size=4, default_value=(0, False, None), concrete=False, artificial=True), + # Register(name="cc_dep1", size=4, concrete=False, artificial=True), + # Register(name="cc_dep2", size=4, concrete=False, artificial=True), + # Register(name="cc_ndep", size=4, concrete=False, artificial=True), + Register(name="d", size=4, alias_names=("dflag",), default_value=(1, False, None), concrete=False, + ), + Register(name="id", size=4, alias_names=("idflag",), default_value=(1, False, None), concrete=False, + ), + Register(name="ac", size=4, alias_names=("acflag",), default_value=(0, False, None), concrete=False, + ), + Register(name="eip", size=4, alias_names=("pc"), subregisters=[("ip", 0, 2)], + ), + Register( + name="fpreg", + size=64, + subregisters=[ + ("mm0", 0, 8), + ("mm1", 8, 8), + ("mm2", 16, 8), + ("mm3", 24, 8), + ("mm4", 32, 8), + ("mm5", 40, 8), + ("mm6", 48, 8), + ("mm7", 56, 8), + ], + alias_names=("fpu_regs",), + floating_point=True, + concrete=False, + ), + Register(name="fptag", size=8, alias_names=("fpu_tags",), floating_point=True, default_value=(0, False, None), + ), + Register(name="fpround", size=4, floating_point=True, default_value=(0, False, None), + ), + Register(name="fc3210", size=4, floating_point=True), + # Register(name="ftop", size=4, floating_point=True, default_value=(7, False, None), artificial=True), + #Register(name="sseround", size=4, vector=True, default_value=(0, False, None), + # vex_offset=72, + # ), + Register(name="cs", size=2), + Register(name="ds", size=2), + Register(name="es", size=2), + Register(name="fs", size=2, default_value=(0, False, None), concrete=False), + Register(name="gs", size=2, default_value=(0, False, None), concrete=False), + Register(name="ss", size=2), + # Register(name="ldt", size=8, default_value=(0, False, None), concrete=False), + # Register(name="gdt", size=8, default_value=(0, False, None), concrete=False), + # Register(name="emnote", size=4, artificial=True), + Register(name="cmstart", size=4), + Register(name="cmlen", size=4), + Register(name="nraddr", size=4), + Register(name="sc_class", size=4), + Register(name="ip_at_syscall", size=4, concrete=False, artificial=True), + Register( + name="eflags", + size=4, + subregisters=[("flags", 0, 2)], + ), + ] + + @property + def capstone_x86_syntax(self): + """Get the current syntax Capstone uses for x86. It can be 'intel' or 'at&t' + + :return: Capstone's current x86 syntax + :rtype: str + """ + return self._cs_x86_syntax + + @capstone_x86_syntax.setter + def capstone_x86_syntax(self, new_syntax): + """Set the syntax that Capstone outputs for x86. + """ + if new_syntax not in ("intel", "at&t"): + raise ArchError('Unsupported Capstone x86 syntax. It must be either "intel" or "at&t".') + + if new_syntax != self._cs_x86_syntax: + self._cs = None + self._cs_x86_syntax = new_syntax + + def _configure_capstone(self): + self._cs.syntax = ( + _capstone.CS_OPT_SYNTAX_ATT if self._cs_x86_syntax == "at&t" else _capstone.CS_OPT_SYNTAX_INTEL + ) + + @property + def keystone_x86_syntax(self): + """Get the current syntax Keystone uses for x86. It can be 'intel', + 'at&t', 'nasm', 'masm', 'gas' or 'radix16' + + :return: Keystone's current x86 syntax + :rtype: str + """ + return self._ks_x86_syntax + + @keystone_x86_syntax.setter + def keystone_x86_syntax(self, new_syntax): + """Set the syntax that Keystone uses for x86. + """ + if new_syntax not in ("intel", "at&t", "nasm", "masm", "gas", "radix16"): + raise ArchError( + "Unsupported Keystone x86 syntax. It must be one of the following: " + '"intel", "at&t", "nasm", "masm", "gas" or "radix16".', + ) + + if new_syntax != self._ks_x86_syntax: + self._ks = None + self._ks_x86_syntax = new_syntax + + def _configure_keystone(self): + if self._ks_x86_syntax == "at&t": + self._ks.syntax = _keystone.KS_OPT_SYNTAX_ATT + elif self._ks_x86_syntax == "nasm": + self._ks.syntax = _keystone.KS_OPT_SYNTAX_NASM + elif self._ks_x86_syntax == "masm": + self._ks.syntax = _keystone.KS_OPT_SYNTAX_MASM + elif self._ks_x86_syntax == "gas": + self._ks.syntax = _keystone.KS_OPT_SYNTAX_GAS + elif self._ks_x86_syntax == "radix16": + self._ks.syntax = _keystone.KS_OPT_SYNTAX_RADIX16 + else: + self._ks.syntax = _keystone.KS_OPT_SYNTAX_INTEL + + +register_arch([r"86_16"], 16, "Iend_LE", Arch86_16) diff --git a/angr_platforms/X86_16/cr.py b/angr_platforms/X86_16/cr.py new file mode 100644 index 0000000..0fbd703 --- /dev/null +++ b/angr_platforms/X86_16/cr.py @@ -0,0 +1,33 @@ +class CR: + def __init__(self): + self.cr0 = 0 + self.cr1 = 0 + self.cr2 = 0 + self.cr3 = 0 + self.cr4 = 0 + + def get_crn(self, n: int) -> int: + if n == 0: + return self.cr0 + elif n == 1: + return self.cr1 + elif n == 2: + return self.cr2 + elif n == 3: + return self.cr3 + elif n == 4: + return self.cr4 + else: + raise ValueError(f"Invalid CR index: {n}") + + def set_crn(self, n: int, value: int): + pass + + def is_protected(self) -> bool: + return False + + def is_ena_paging(self) -> bool: + return bool(self.cr0 & (1 << 31)) # PG bit + + def get_pdir_base(self) -> int: + return (self.cr3 >> 12) & 0xFFFFF000 # Page Directory Base diff --git a/angr_platforms/X86_16/debug.py b/angr_platforms/X86_16/debug.py new file mode 100644 index 0000000..6d26420 --- /dev/null +++ b/angr_platforms/X86_16/debug.py @@ -0,0 +1,63 @@ +import sys +import traceback + +# Constants for debug message types +F_ASSERT = 0 +F_ERROR = 1 +F_WARN = 2 +F_INFO = 3 +F_MSG = 4 + +# Global debug level +debug_level = 0 + +def debug_print(type, file, function, line, level, fmt, *args): + """Prints debug messages based on the message type and debug level.""" + typeset = { + F_ASSERT: ("ASSERT", sys.stderr, True), + F_ERROR: ("ERROR", sys.stderr, True), + F_WARN: ("WARN", sys.stderr, False), + F_INFO: ("INFO", sys.stdout, False), + F_MSG: (None, sys.stdout, False), + } + + name, fp, fatal = typeset[type] + + if fatal or (level > 0 and (1 << (level - 1)) & debug_level): + if name: + print(f"[{name}{f'_{level}' if level else ''}] ", end="", file=fp) + print(f"{function} ({file}:{line}) ", end="", file=fp) + print(fmt % args, file=fp) + if fatal: + traceback.print_stack() + sys.exit(-1) + +def ASSERT(cond): + """Asserts a condition and prints an error message if it fails.""" + if not cond: + debug_print(F_ASSERT, *traceback.extract_stack()[-2], cond) + +def ERROR(fmt, *args): + """Prints an error message and terminates the program.""" + debug_print(F_ERROR, *traceback.extract_stack()[-2], fmt, *args) + +def WARN(fmt, *args): + """Prints a warning message.""" + debug_print(F_WARN, *traceback.extract_stack()[-2], 0, fmt, *args) + +def INFO(level, fmt, *args): + """Prints an informational message based on the debug level.""" + debug_print(F_INFO, *traceback.extract_stack()[-2], level, fmt, *args) + +def DEBUG_MSG(level, fmt, *args): + """Prints a debug message based on the debug level.""" + debug_print(F_MSG, *traceback.extract_stack()[-2], level, fmt, *args) + +def MSG(fmt, *args): + """Prints a regular message to stdout.""" + print(fmt % args, file=sys.stdout) + +def set_debuglv(verbose): + """Sets the global debug level.""" + global debug_level + debug_level = int(verbose) diff --git a/angr_platforms/X86_16/dev_io.py b/angr_platforms/X86_16/dev_io.py new file mode 100644 index 0000000..61532a3 --- /dev/null +++ b/angr_platforms/X86_16/dev_io.py @@ -0,0 +1,59 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from .memory import Memory + + +class PortIO(ABC): + @abstractmethod + def in8(self, addr: int) -> int: + """Reads an 8-bit value from the specified port address.""" + + @abstractmethod + def out8(self, addr: int, value: int): + """Writes an 8-bit value to the specified port address.""" + + +class MemoryIO(ABC): + def __init__(self): + self.memory: Optional[Memory] = None + self.paddr = 0 + self.size = 0 + + def set_mem(self, mem: Memory, addr: int, length: int): + """Sets the memory object, base address, and size for the device.""" + self.memory = mem + self.paddr = addr + self.size = length + + def read32(self, offset: int) -> int: + """Reads a 32-bit value from the specified offset.""" + value = 0 + for i in range(4): + value += self.read8(offset + i) << (8 * i) + return value + + def read16(self, offset: int) -> int: + """Reads a 16-bit value from the specified offset.""" + value = 0 + for i in range(2): + value += self.read8(offset + i) << (8 * i) + return value + + @abstractmethod + def read8(self, offset: int) -> int: + """Reads an 8-bit value from the specified offset.""" + + def write32(self, offset: int, value: int): + """Writes a 32-bit value to the specified offset.""" + for i in range(4): + self.write8(offset + i, (value >> (8 * i)) & 0xFF) + + def write16(self, offset: int, value: int): + """Writes a 16-bit value to the specified offset.""" + for i in range(2): + self.write8(offset + i, (value >> (8 * i)) & 0xFF) + + @abstractmethod + def write8(self, offset: int, value: int): + """Writes an 8-bit value to the specified offset.""" diff --git a/angr_platforms/X86_16/eflags.py b/angr_platforms/X86_16/eflags.py new file mode 100644 index 0000000..8238c94 --- /dev/null +++ b/angr_platforms/X86_16/eflags.py @@ -0,0 +1,281 @@ +from pyvex.lifting.util.vex_helper import Type + +from .regs import reg16_t, reg32_t + + +class Eflags: + def __init__(self): + #self.eflags = 0 + pass + + def get_eflags(self): + return self.get_gpreg(reg32_t.EFLAGS) + + def set_eflags(self, v): + self.set_gpreg(reg32_t.EFLAGS, v) + + def get_flags(self): + return self.get_gpreg(reg16_t.FLAGS) + + def set_flags(self, v): + self.set_gpreg(reg16_t.FLAGS, v) + + def get_flag(self, idx): + return self.get_gpreg(reg16_t.FLAGS)[idx].cast_to(Type.int_1) + + def is_carry(self): + return self.get_flag(0) + + def is_parity(self): + return self.get_flag(2) + + def is_zero(self): + return self.get_flag(6) + + def is_sign(self): + return self.get_flag(7) + + def is_overflow(self): + return self.get_flag(11) + + def is_interrupt(self): + return self.get_flag(9) + + def is_direction(self): + return self.get_flag(10) + + @staticmethod + def set_flag(flags, idx, value): + #value = value.cast_to(Type.int_1) + return flags & ~(1 << idx) | (value.cast_to(Type.int_16) << idx) + + def set_carry(self, flags, carry): + return self.set_flag(flags, 0, carry) + + def set_parity(self, flags, parity): + return + eflags = self.get_gpreg(reg16_t.FLAGS) + eflags[self.constant(2)] = parity.cast_to(Type.int_16) + self.set_gpreg(reg16_t.FLAGS, flags) + + def set_zero(self, flags, zero): + return self.set_flag(flags, 6, zero) + + def set_sign(self, flags, sign): + return self.set_flag(flags, 7, sign) + + def set_overflow(self, flags, over): + return self.set_flag(flags, 11, over) + + def set_interrupt(self, interrupt): + flags = self.get_gpreg(reg16_t.FLAGS) + interrupt = self.constant(interrupt, Type.int_1) + flags = self.set_flag(flags, 9, interrupt) + self.set_gpreg(reg16_t.FLAGS, flags) + + def set_direction(self, direction): + flags = self.get_gpreg(reg16_t.FLAGS) + direction = self.constant(direction, Type.int_1) + flags = self.set_flag(flags, 10, direction) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_inc(self, v1): + flags = self.get_gpreg(reg16_t.FLAGS) + size = v1.width + result = v1 + + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, v1 == self.constant(1 << (size - 1), v1.ty)) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_add(self, v1, v2): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + flags = self.get_gpreg(reg16_t.FLAGS) + size = v1.width + result = v1 + v2 + + flags = self.set_carry(flags, result < v1) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, + ~(~(1 ^ v1[size - 1] ^ v2[size - 1]) | ~((v1 ^ (v1 + v2))[size - 1])), + ) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_adc(self, v1, v2, carry): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + flags = self.get_gpreg(reg16_t.FLAGS) + size = v1.width + result = v1 + v2 + carry + + flags = self.set_carry(flags, result < v1) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, + ~(~(1 ^ v1[size - 1] ^ v2[size - 1]) | ~((v1 ^ (v1 + v2))[size - 1])), + ) + self.set_gpreg(reg16_t.FLAGS, flags) + + + def update_eflags_or(self, v1, v2): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1 | v2 + size = v1.width + + flags = self.set_carry(flags, self.constant(0)) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, self.constant(0)) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_and(self, v1, v2): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1 & v2 + size = v1.width + + flags = self.set_carry(flags, self.constant(0)) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, self.constant(0)) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_sub(self, v1, v2): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1 - v2 + size = v1.width + + flags = self.set_carry(flags, v2 > v1) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, + ~(~(v1[size-1] ^ v2[size-1]) | ~(((v1 & (1<<(size-1))) ^ v1 + v2 * -1)[size-1])), + ) + self.set_gpreg(reg16_t.FLAGS, flags) + + + def update_eflags_sbb(self, v1, v2, c): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1 - v2 - c + size = v1.width + + flags = self.set_carry(flags, (v2 >= v1) if c else (v2 > v1)) # TODO + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, + ~(~(v1[size-1] ^ v2[size-1]) | ~(((v1 & (1<<(size-1))) ^ v1 + (v2 + c) * -1)[size-1])), + ) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_xor(self, v1, v2): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1 ^ v2 + size = v1.width + + flags = self.set_carry(flags, self.constant(0)) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, self.constant(0)) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_neg(self, v2): + flags = self.get_gpreg(reg16_t.FLAGS) + result = (v2 * -1).cast_to(Type.int_16) + size = v2.width + + flags = self.set_carry(flags, v2 != 0) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, + ~(~v2[size - 1] | (~(v2 * -1).cast_to(Type.int_16))[size - 1]), + ) + # v2 == (self.constant(1 << (size - 1), v2.ty)) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_dec(self, v1): + v2 = self.constant(1, v1.ty) + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1 - v2 + size = v1.width + + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, v1 == (self.constant(1 << (size - 1), v1.ty))) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_mul(self, v1, v2): + type1 = v1.ty + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1.cast_to(Type.int_32) * v2.cast_to(Type.int_32) + size = v1.width + + flags = self.set_carry(flags, (result >> size) != 0) + flags = self.set_zero(flags, result.cast_to(type1) == 0) + flags = self.set_sign(flags, (v1*v2)[size - 1]) + flags = self.set_overflow(flags, (result >> size) != 0) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_imul(self, v1, v2): + v2 = self.constant(v2, v1.ty) if isinstance(v2, int) else v2 + type1 = v1.ty + flags = self.get_gpreg(reg16_t.FLAGS) + result = v1.widen_signed(Type.int_32) * v2.widen_signed(Type.int_32) + size = v1.width + + sign = (v1.cast_to(v2.ty, signed=True)*v2.signed)[size - 1] + #_7fff = self.constant(0xffff8000, Type.int_32) + #cfof = ((result & _7fff) == 0) and ((result & _7fff) == _7fff) + #cfof = (result.signed >> 16).signed == (result.signed >> 15).signed + #flags = self.set_carry(flags, cfof) + flags = self.set_zero(flags, result.cast_to(type1) == 0) + flags = self.set_sign(flags, sign) + #flags = self.set_overflow(flags, cfof) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_shl(self, v, c): + #if c == 0: + # return + flags = self.get_gpreg(reg16_t.FLAGS) + result = v << c + size = v.width + + flags = self.set_carry(flags, (v >> (size - c - 1)) & 1) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + flags = self.set_overflow(flags, (result ^ v)[size - 1]) + self.set_gpreg(reg16_t.FLAGS, flags) + + def update_eflags_shr(self, v, c): + flags = self.get_gpreg(reg16_t.FLAGS) + result = v >> c + size = v.width + + flags = self.set_carry(flags, v >> (c - 1) & 1) + #self.set_parity(flags, self.chk_parity(result & 0xFF)) + flags = self.set_zero(flags, result == 0) + flags = self.set_sign(flags, result[size - 1]) + if c == 1: + flags = self.set_overflow(flags, v >> (size - 1) & 1) + self.set_gpreg(reg16_t.FLAGS, flags) + + def chk_parity(self, v): + return None + p = self.constant(1, Type.int_1) + for i in range(8): + p ^= v[i].cast_to(Type.int_1) + return p diff --git a/angr_platforms/X86_16/emu.py b/angr_platforms/X86_16/emu.py new file mode 100644 index 0000000..48cac7d --- /dev/null +++ b/angr_platforms/X86_16/emu.py @@ -0,0 +1,60 @@ +from typing import Any, Dict + +from .emulator import Emulator +from .instruction import InstrData, X86Instruction +from .regs import reg32_t, sgreg_t + + +class EmuInstr(X86Instruction): + def __init__(self, emu: Emulator, instr: InstrData, mode32: bool): + super().__init__(emu, instr, mode32) + + def type_descriptor(self, instr: Dict[str, Any], sel: int) -> int: + raise NotImplementedError + + def set_ldtr(self, sel: int) -> None: + raise NotImplementedError + + def set_tr(self, sel: int) -> None: + raise NotImplementedError + + def switch_task(self, sel: int) -> None: + raise NotImplementedError + + def jmpf(self, instr: Dict[str, Any], sel: int, eip: int) -> None: + self.emu.set_segment(sgreg_t.CS.name, sel) + self.emu.set_eip(eip) + + def callf(self, instr: Dict[str, Any], sel: int, eip: int) -> None: + cs = self.emu.get_segment(sgreg_t.CS.name) + RPL = sel & 3 + CPL = cs & 3 + + if CPL != RPL: + if RPL < CPL: + raise Exception(self.emu.EXP_GP) + self.emu.push32(self.emu.get_segment(sgreg_t.SS.name)) + self.emu.push32(self.emu.get_gpreg(reg32_t.ESP.name)) + + self.emu.push32(cs) + self.emu.push32(self.emu.get_eip()) + + self.emu.set_segment(sgreg_t.CS.name, sel) + self.emu.set_eip(eip) + + def retf(self, instr: Dict[str, Any]) -> None: + ip = self.emu.pop16() + cs = self.emu.pop16() + self.emu.set_segment(sgreg_t.CS.name, cs) + self.emu.set_ip(ip) + + def iret(self, instr: Dict[str, Any]) -> None: + ip = self.emu.pop16() + cs = self.emu.pop16() + flags = self.emu.pop16() + self.emu.set_flags(flags) + self.emu.set_segment(sgreg_t.CS.name, cs) + self.emu.set_ip(ip) + + def chk_ring(self, dpl: int) -> bool: + raise NotImplementedError diff --git a/angr_platforms/X86_16/emulator.py b/angr_platforms/X86_16/emulator.py new file mode 100644 index 0000000..0a0b1df --- /dev/null +++ b/angr_platforms/X86_16/emulator.py @@ -0,0 +1,8 @@ +from .interrupt import Interrupt + + +class Emulator(Interrupt): #DataAccess, + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + diff --git a/angr_platforms/X86_16/exception.py b/angr_platforms/X86_16/exception.py new file mode 100644 index 0000000..a92827a --- /dev/null +++ b/angr_platforms/X86_16/exception.py @@ -0,0 +1,34 @@ +# Exception types +EXP_DE = 0 # Divide Error +EXP_DB = 1 # Debug +EXP_BP = 3 # Breakpoint +EXP_OF = 4 # Overflow +EXP_BR = 5 # BOUND Range Exceeded +EXP_UD = 6 # Invalid Opcode +EXP_NM = 7 # Device Not Available +EXP_DF = 8 # Double Fault +EXP_TS = 10 # Invalid TSS +EXP_NP = 11 # Segment Not Present +EXP_SS = 12 # Stack-Segment Fault +EXP_GP = 13 # General Protection +EXP_PF = 14 # Page Fault +EXP_MF = 16 # x87 FPU Floating-Point Error +EXP_AC = 17 # Alignment Check +EXP_MC = 18 # Machine Check +EXP_XF = 19 # SIMD Floating-Point Exception +EXP_VE = 20 # Virtualization Exception +EXP_SX = 30 # Security Exception + +# Helper functions for raising exceptions + +def EXCEPTION(n, c): + if c: + print(f"WARN: Exception interrupt {n} ({c})") + raise Exception(n) + + +def EXCEPTION_WITH(n, c, e): + if c: + print(f"WARN: Exception interrupt {n} ({c})") + e() + raise Exception(n) diff --git a/angr_platforms/X86_16/exec.py b/angr_platforms/X86_16/exec.py new file mode 100644 index 0000000..1089787 --- /dev/null +++ b/angr_platforms/X86_16/exec.py @@ -0,0 +1,208 @@ +import sys + +from .regs import reg8_t, reg16_t, reg32_t, sgreg_t + +from .instruction import X86Instruction + + +class ExecInstr(X86Instruction): + def __init__(self, emu): + self.instrfuncs = [None] * 0x200 # Initialize with None for all opcodes + #self.chsz_ad = False + + def exec(self): + opcode = self.instr.opcode + + if opcode >> 8 == 0x0f: + opcode = (opcode & 0xff) | 0x0100 + + if self.instrfuncs[opcode] is None: + print(f"not implemented OPCODE 0x{opcode:02x}", file=sys.stderr) + return False + + self.instrfuncs[opcode]() + return True + + def set_rm32(self, value): + if self.instr.modrm.mod == 3: + self.emu.set_gpreg(reg32_t(self.instr.modrm.rm), value) + else: + addr = self.calc_modrm() + seg = self.select_segment() + self.emu.put_data32(seg, addr, value) + + + def get_rm32(self): + if self.instr.modrm.mod == 3: + return self.emu.get_gpreg(reg32_t(self.instr.modrm.rm)) + else: + addr = self.calc_modrm() + seg = self.select_segment() + return self.emu.get_data32(seg, addr) + + def set_r32(self, value): + self.emu.set_gpreg(reg32_t(self.instr.modrm.reg), value) + + def get_r32(self): + return self.emu.get_gpreg(reg32_t(self.instr.modrm.reg)) + + def set_moffs32(self, value): + self.instr.segment = sgreg_t.DS.value + self.emu.put_data32(self.select_segment(), self.instr.moffs, value) + + def get_moffs32(self): + self.instr.segment = sgreg_t.DS.value + return self.emu.get_data32(self.select_segment(), self.instr.moffs) + + def set_rm16(self, value): + if self.instr.modrm.mod == 3: + self.emu.set_gpreg(reg16_t(self.instr.modrm.rm), value) + else: + addr = self.calc_modrm() + seg = self.select_segment() + self.emu.put_data16(seg, addr, value) + + def get_rm16(self): + if self.instr.modrm.mod == 3: + return self.emu.get_gpreg(reg16_t(self.instr.modrm.rm)) + else: + addr = self.calc_modrm() + seg = self.select_segment() + return self.emu.get_data16(seg, addr) + + def set_r16(self, value): + self.emu.set_gpreg(reg16_t(self.instr.modrm.reg), value) + + def get_r16(self): + return self.emu.get_gpreg(reg16_t(self.instr.modrm.reg)) + + def set_moffs16(self, value): + self.instr.segment = sgreg_t.DS.value + self.emu.put_data16(self.select_segment(), self.instr.moffs, value) + + def get_moffs16(self): + self.instr.segment = sgreg_t.DS.value + return self.emu.get_data16(self.select_segment(), self.instr.moffs) + + def set_rm8(self, value): + if self.instr.modrm.mod == 3: + self.emu.set_gpreg(reg8_t(self.instr.modrm.rm), value) + else: + addr = self.calc_modrm() + seg = self.select_segment() + self.emu.put_data8(seg, addr, value) + + def get_rm8(self): + if self.instr.modrm.mod == 3: + return self.emu.get_gpreg(reg8_t(self.instr.modrm.rm)) + else: + addr = self.calc_modrm() + seg = self.select_segment() + return self.emu.get_data8(seg, addr) + + def set_r8(self, value): + self.emu.set_gpreg(reg8_t(self.instr.modrm.reg), value) + + def get_r8(self): + return self.emu.get_gpreg(reg8_t(self.instr.modrm.reg)) + + def set_moffs8(self, value): + self.instr.segment = sgreg_t.DS.value + self.emu.put_data16(self.select_segment(), self.instr.moffs, value) + + def get_moffs8(self): + self.instr.segment = sgreg_t.DS.value + return self.emu.get_data8(self.select_segment(), self.instr.moffs) + + def get_m(self): + return self.calc_modrm() + + def set_sreg(self, value): + self.emu.set_segment(sgreg_t(self.instr.modrm.reg), value) + + def get_sreg(self): + return self.emu.get_segment(sgreg_t(self.instr.modrm.reg)) + + def set_crn(self, value): + print(f"set CR{self.instr.modrm.reg} = {value:x}") + self.emu.set_crn(self.instr.modrm.reg, value) + + def get_crn(self): + return self.emu.get_crn(self.instr.modrm.reg) + + def calc_modrm(self): + assert self.instr.modrm.mod != 3 + + self.instr.segment = sgreg_t.DS.value + if self.emu.is_mode32() ^ self.chsz_ad: + return self.calc_modrm32() + else: + return self.calc_modrm16() + + def calc_modrm16(self): + addr = 0 + + if self.instr.modrm.mod == 1: + addr += self.instr.disp8 + elif self.instr.modrm.mod == 2: + addr += self.instr.disp16 + + rm = self.instr.modrm.rm + if rm in (0, 1, 7): + addr += self.emu.get_gpreg(reg16_t.BX) + elif rm in (2, 3, 6): + if self.instr.modrm.mod == 0 and rm == 6: + addr += self.instr.disp16 + else: + addr += self.emu.get_gpreg(reg16_t.BP) + self.instr.segment = sgreg_t.SS.value + + if rm < 6: + if rm % 2: + addr += self.emu.get_gpreg(reg16_t.DI) + else: + addr += self.emu.get_gpreg(reg16_t.SI) + + return addr + + def calc_modrm32(self): + addr = 0 + + if self.instr.modrm.mod == 1: + addr += self.instr.disp8 + elif self.instr.modrm.mod == 2: + addr += self.instr.disp32 + + rm = self.instr.modrm.rm + if rm == 4: + addr += self.calc_sib() + elif rm == 5: + if self.instr.modrm.mod == 0: + addr += self.instr.disp32 + else: + self.instr.segment = sgreg_t.SS.value + addr += self.emu.get_gpreg(reg32_t(rm)) + else: + self.instr.segment = sgreg_t.DS.value + addr += self.emu.get_gpreg(reg32_t(rm)) + + return addr + + def calc_sib(self): + base = 0 + + if self.instr.sib.base == 5 and self.instr.modrm.mod == 0: + base = self.instr.disp32 + elif self.instr.sib.base == 4: + if self.instr.sib.scale == 0: + self.instr.segment = sgreg_t.SS.value + else: + print( + f"not implemented SIB (base = {self.instr.sib.base}, index = {self.instr.sib.index}, scale = {self.instr.sib.scale})\n", + file=sys.stderr, + ) + else: + self.instr.segment = sgreg_t.DS.value if self.instr.modrm.rm != 5 else sgreg_t.SS.value + base = self.emu.get_gpreg(reg32_t(self.instr.sib.base)) + + return base + self.emu.get_gpreg(reg32_t(self.instr.sib.index)) * (1 << self.instr.sib.scale) diff --git a/angr_platforms/X86_16/hardware.py b/angr_platforms/X86_16/hardware.py new file mode 100644 index 0000000..b9ee69d --- /dev/null +++ b/angr_platforms/X86_16/hardware.py @@ -0,0 +1,11 @@ +from .cr import CR +from .io import IO +from .memory import Memory +from .processor import Processor + + +class Hardware(Processor, Memory, IO): + def __init__(self, size: int = 0): + super(Hardware, self).__init__() # Processor + super(CR, self).__init__(size) # Memory + super(Memory, self).__init__(self) # IO diff --git a/angr_platforms/X86_16/instr16.py b/angr_platforms/X86_16/instr16.py new file mode 100644 index 0000000..844727c --- /dev/null +++ b/angr_platforms/X86_16/instr16.py @@ -0,0 +1,992 @@ + +from pyvex.lifting.util import JumpKind, Type + +from .instr_base import InstrBase +from .instruction import * +from .regs import reg8_t, reg16_t, sgreg_t + + +class Instr16(InstrBase): + def __init__(self, emu: Emulator, instr: InstrData): + super().__init__(emu, instr, mode32=False) # X86Instruction + + self.set_funcflag(0x01, self.add_rm16_r16, CHK_MODRM) + self.set_funcflag(0x03, self.add_r16_rm16, CHK_MODRM) + self.set_funcflag(0x05, self.add_ax_imm16, CHK_IMM16) + self.set_funcflag(0x06, self.push_es, 0) + self.set_funcflag(0x07, self.pop_es, 0) + self.set_funcflag(0x09, self.or_rm16_r16, CHK_MODRM) + self.set_funcflag(0x0B, self.or_r16_rm16, CHK_MODRM) + self.set_funcflag(0x0D, self.or_ax_imm16, CHK_IMM16) + self.set_funcflag(0x0E, self.push_cs, 0) + self.set_funcflag(0x11, self.adc_rm16_r16, CHK_MODRM) + self.set_funcflag(0x13, self.adc_r16_rm16, CHK_MODRM) + self.set_funcflag(0x16, self.push_ss, 0) + self.set_funcflag(0x17, self.pop_ss, 0) + self.set_funcflag(0x19, self.sbb_rm16_r16, CHK_MODRM) + self.set_funcflag(0x1B, self.sbb_r16_rm16, CHK_MODRM) + self.set_funcflag(0x1E, self.push_ds, 0) + self.set_funcflag(0x1F, self.pop_ds, 0) + self.set_funcflag(0x21, self.and_rm16_r16, CHK_MODRM) + self.set_funcflag(0x23, self.and_r16_rm16, CHK_MODRM) + self.set_funcflag(0x25, self.and_ax_imm16, CHK_IMM16) + self.set_funcflag(0x29, self.sub_rm16_r16, CHK_MODRM) + self.set_funcflag(0x2B, self.sub_r16_rm16, CHK_MODRM) + self.set_funcflag(0x2D, self.sub_ax_imm16, CHK_IMM16) + self.set_funcflag(0x31, self.xor_rm16_r16, CHK_MODRM) + self.set_funcflag(0x33, self.xor_r16_rm16, CHK_MODRM) + self.set_funcflag(0x35, self.xor_ax_imm16, CHK_IMM16) + self.set_funcflag(0x39, self.cmp_rm16_r16, CHK_MODRM) + self.set_funcflag(0x3B, self.cmp_r16_rm16, CHK_MODRM) + self.set_funcflag(0x3D, self.cmp_ax_imm16, CHK_IMM16) + + for i in range(8): + self.set_funcflag(0x40+i, self.inc_r16, 0) + self.set_funcflag(0x48+i, self.dec_r16, 0) + self.set_funcflag(0x50+i, self.push_r16, 0) + self.set_funcflag(0x58+i, self.pop_r16, 0) + + self.set_funcflag(0x60, self.pusha, 0) + self.set_funcflag(0x61, self.popa, 0) + self.set_funcflag(0x68, self.push_imm16, CHK_IMM16) + self.set_funcflag(0x69, self.imul_r16_rm16_imm16, CHK_MODRM | CHK_IMM16) + self.set_funcflag(0x6A, self.push_imm8, CHK_IMM8) + self.set_funcflag(0x6B, self.imul_r16_rm16_imm8, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0x85, self.test_rm16_r16, CHK_MODRM) + self.set_funcflag(0x87, self.xchg_r16_rm16, CHK_MODRM) + self.set_funcflag(0x89, self.mov_rm16_r16, CHK_MODRM) + self.set_funcflag(0x8B, self.mov_r16_rm16, CHK_MODRM) + self.set_funcflag(0x8C, self.mov_rm16_sreg, CHK_MODRM) + self.set_funcflag(0x8D, self.lea_r16_m16, CHK_MODRM) + + for i in range(1, 8): + self.set_funcflag(0x90+i, self.xchg_r16_ax, 0) + + self.set_funcflag(0x98, self.cbw, 0) + self.set_funcflag(0x99, self.cwd, 0) + self.set_funcflag(0x9A, self.callf_ptr16_16, CHK_PTR16 | CHK_IMM16) + self.set_funcflag(0x9C, self.pushf, 0) + self.set_funcflag(0x9D, self.popf, 0) + self.set_funcflag(0xA1, self.mov_ax_moffs16, CHK_MOFFS) + self.set_funcflag(0xA3, self.mov_moffs16_ax, CHK_MOFFS) + self.set_funcflag(0xA5, self.movsw_m16_m16, 0) + self.set_funcflag(0xA6, self.cmps_m8_m8, 0) + self.set_funcflag(0xA7, self.cmps_m16_m16, 0) + self.set_funcflag(0xA9, self.test_ax_imm16, CHK_IMM16) + + for i in range(8): + self.set_funcflag(0xB8+i, self.mov_r16_imm16, CHK_IMM16) + + self.set_funcflag(0xC3, self.ret, 0) + self.set_funcflag(0xC4, self.les_es_r16_m16, CHK_MODRM) + self.set_funcflag(0xC7, self.mov_rm16_imm16, CHK_MODRM | CHK_IMM16) + self.set_funcflag(0xC9, self.leave, 0) + self.set_funcflag(0xE0, self.loop16ne, CHK_IMM8) + self.set_funcflag(0xE1, self.loop16e, CHK_IMM8) + self.set_funcflag(0xE2, self.loop16, CHK_IMM8) + self.set_funcflag(0xE3, self.jcxz_rel8, CHK_IMM8) + self.set_funcflag(0xE5, self.in_ax_imm8, CHK_IMM8) + self.set_funcflag(0xE7, self.out_imm8_ax, CHK_IMM8) + self.set_funcflag(0xE8, self.call_rel16, CHK_IMM16) + self.set_funcflag(0xE9, self.jmp_rel16, CHK_IMM16) + self.set_funcflag(0xEA, self.jmpf_ptr16_16, CHK_PTR16 | CHK_IMM16) + self.set_funcflag(0xED, self.in_ax_dx, 0) + self.set_funcflag(0xEF, self.out_dx_ax, 0) + + self.set_funcflag(0x0F80, self.jo_rel16, CHK_IMM16) + self.set_funcflag(0x0F81, self.jno_rel16, CHK_IMM16) + self.set_funcflag(0x0F82, self.jb_rel16, CHK_IMM16) + self.set_funcflag(0x0F83, self.jnb_rel16, CHK_IMM16) + self.set_funcflag(0x0F84, self.jz_rel16, CHK_IMM16) + self.set_funcflag(0x0F85, self.jnz_rel16, CHK_IMM16) + self.set_funcflag(0x0F86, self.jbe_rel16, CHK_IMM16) + self.set_funcflag(0x0F87, self.ja_rel16, CHK_IMM16) + self.set_funcflag(0x0F88, self.js_rel16, CHK_IMM16) + self.set_funcflag(0x0F89, self.jns_rel16, CHK_IMM16) + self.set_funcflag(0x0F8A, self.jp_rel16, CHK_IMM16) + self.set_funcflag(0x0F8B, self.jnp_rel16, CHK_IMM16) + self.set_funcflag(0x0F8C, self.jl_rel16, CHK_IMM16) + self.set_funcflag(0x0F8D, self.jnl_rel16, CHK_IMM16) + self.set_funcflag(0x0F8E, self.jle_rel16, CHK_IMM16) + self.set_funcflag(0x0F8F, self.jnle_rel16, CHK_IMM16) + self.set_funcflag(0x0FAF, self.imul_r16_rm16, CHK_MODRM) + self.set_funcflag(0x0FB6, self.movzx_r16_rm8, CHK_MODRM) + self.set_funcflag(0x0FB7, self.movzx_r16_rm16, CHK_MODRM) + self.set_funcflag(0x0FBE, self.movsx_r16_rm8, CHK_MODRM) + self.set_funcflag(0x0FBF, self.movsx_r16_rm16, CHK_MODRM) + + self.set_funcflag(0x81, self.code_81, CHK_MODRM | CHK_IMM16) + self.set_funcflag(0x83, self.code_83, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0xC1, self.code_c1, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0xD1, self.code_d1, CHK_MODRM) + self.set_funcflag(0xD3, self.code_d3, CHK_MODRM) + self.set_funcflag(0xF7, self.code_f7, CHK_MODRM) + self.set_funcflag(0xFF, self.code_ff, CHK_MODRM) + self.set_funcflag(0x0F00, self.code_0f00, CHK_MODRM) + self.set_funcflag(0x0F01, self.code_0f01, CHK_MODRM) + + + def jcxz_rel8(self) -> None: + cx = self.emu.get_gpreg(reg16_t.CX) + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not (cx == 0), ip) + + + def loop16(self) -> None: + cx = self.emu.get_gpreg(reg16_t.CX) + cx -= 1 + self.emu.set_gpreg(reg16_t.CX, cx) + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8, Type.int_8).signed + 2 + self.emu.lifter_instruction.jump(cx != 0, ip, JumpKind.Boring) + + def loop16e(self) -> None: + cx = self.emu.get_gpreg(reg16_t.CX) + cx -= 1 + self.emu.set_gpreg(reg16_t.CX, cx) + zero = self.emu.is_zero() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8, Type.int_8).signed + 2 + self.emu.lifter_instruction.jump(cx and zero, ip, JumpKind.Boring) + + def loop16ne(self) -> None: + cx = self.emu.get_gpreg(reg16_t.CX) + cx -= 1 + self.emu.set_gpreg(reg16_t.CX, cx) + zero = self.emu.is_zero() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8, Type.int_8).signed + 2 + self.emu.lifter_instruction.jump(cx and not zero, ip, JumpKind.Boring) + + def sbb_r16_rm16(self) -> None: + r16 = self.get_r16() + rm16 = self.get_rm16() + carry = self.emu.is_carry().cast_to(Type.int_16) + self.set_r16(r16 - rm16 - carry) + self.emu.update_eflags_sbb(r16, rm16, carry) + + def add_rm16_r16(self): + rm16 = self.get_rm16() + r16 = self.get_r16() + self.set_rm16(rm16 + r16) + self.emu.update_eflags_add(rm16, r16) + + def sbb_rm16_r16(self) -> None: + rm16 = self.get_rm16() + r16 = self.get_r16() + carry = self.emu.is_carry().cast_to(Type.int_16) + self.set_rm8(rm16 - r16 - carry) + self.emu.update_eflags_sbb(rm16, r16, carry) + + def adc_rm16_r16(self) -> None: + rm16 = self.get_rm16() + r16 = self.get_r16() + carry = self.emu.is_carry().cast_to(Type.int_16) + self.set_rm8(rm16 + r16 + carry) + self.emu.update_eflags_adc(rm16, r16, carry) + + def add_r16_rm16(self): + r16 = self.get_r16() + rm16 = self.get_rm16() + self.set_r16(r16 + rm16) + self.emu.update_eflags_add(r16, rm16) + + def adc_r16_rm16(self) -> None: + r16 = self.get_r16() + rm16 = self.get_rm16() + carry = self.emu.is_carry().cast_to(Type.int_16) + self.set_r16(r16 + rm16 + carry) + self.emu.update_eflags_adc(r16, rm16, carry) + + def add_ax_imm16(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t.AX, ax + self.instr.imm16) + self.emu.update_eflags_add(ax, self.instr.imm16) + + def push_es(self): + self.emu.push16(self.emu.get_segment(sgreg_t.ES)) + + def pop_es(self): + self.emu.set_segment(sgreg_t.ES, self.emu.pop16()) + + def or_rm16_r16(self): + rm16 = self.get_rm16() + r16 = self.get_r16() + self.set_rm16(rm16 | r16) + self.emu.update_eflags_or(rm16, r16) + + def or_r16_rm16(self): + r16 = self.get_r16() + rm16 = self.get_rm16() + self.set_r16(r16 | rm16) + self.emu.update_eflags_or(r16, rm16) + + def or_ax_imm16(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t.AX, ax | self.instr.imm16) + self.emu.update_eflags_or(ax, self.instr.imm16) + + def push_cs(self): + self.emu.push16(self.emu.get_segment(sgreg_t.CS)) + + def push_ss(self): + self.emu.push16(self.emu.get_segment(sgreg_t.SS)) + + def pop_ss(self): + self.emu.set_segment(sgreg_t.SS, self.emu.pop16()) + + def push_ds(self): + self.emu.push16(self.emu.get_segment(sgreg_t.DS)) + + def pop_ds(self): + self.emu.set_segment(sgreg_t.DS, self.emu.pop16()) + + def and_rm16_r16(self): + rm16 = self.get_rm16() + r16 = self.get_r16() + self.set_rm16(rm16 & r16) + self.emu.update_eflags_and(rm16, r16) + + def and_r16_rm16(self): + r16 = self.get_r16() + rm16 = self.get_rm16() + self.set_r16(r16 & rm16) + self.emu.update_eflags_and(r16, rm16) + + def and_ax_imm16(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t.AX, ax & self.instr.imm16) + self.emu.update_eflags_and(ax, self.instr.imm16) + + def sub_rm16_r16(self): + rm16 = self.get_rm16() + r16 = self.get_r16() + self.set_rm16(rm16 - r16) + self.emu.update_eflags_sub(rm16, r16) + + def sub_r16_rm16(self): + r16 = self.get_r16() + rm16 = self.get_rm16() + self.set_r16(r16 - rm16) + self.emu.update_eflags_sub(r16, rm16) + + def sub_ax_imm16(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t.AX, ax - self.instr.imm16) + self.emu.update_eflags_sub(ax, self.instr.imm16) + + def xor_rm16_r16(self): + rm16 = self.get_rm16() + r16 = self.get_r16() + self.set_rm16(rm16 ^ r16) + self.emu.update_eflags_xor(rm16, r16) + + + def xor_r16_rm16(self): + r16 = self.get_r16() + rm16 = self.get_rm16() + self.set_r16(r16 ^ rm16) + self.emu.update_eflags_xor(rm16, r16) + + def xor_ax_imm16(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t.AX, ax ^ self.instr.imm16) + self.emu.update_eflags_xor(ax, self.instr.imm16) + + def cmp_rm16_r16(self): + rm16 = self.get_rm16() + r16 = self.get_r16() + self.emu.update_eflags_sub(rm16, r16) + + def cmp_r16_rm16(self): + r16 = self.get_r16() + rm16 = self.get_rm16() + self.emu.update_eflags_sub(r16, rm16) + + def cmp_ax_imm16(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.update_eflags_sub(ax, self.instr.imm16) + + def inc_r16(self): + reg = reg16_t(self.instr.opcode & 0b111) + r16 = self.emu.get_gpreg(reg) + 1 + self.emu.set_gpreg(reg, r16) + self.emu.update_eflags_inc(r16) + + def dec_r16(self): + reg = reg16_t(self.instr.opcode & 0b111) + r16 = self.emu.get_gpreg(reg) + self.emu.set_gpreg(reg, r16 - 1) + self.emu.update_eflags_dec(r16) + + def push_r16(self): + reg = reg16_t(self.instr.opcode & 0b111) + self.emu.push16(self.emu.get_gpreg(reg)) + + def pop_r16(self): + reg = reg16_t(self.instr.opcode & 0b111) + self.emu.set_gpreg(reg, self.emu.pop16()) + + def pusha(self): + sp = self.emu.get_gpreg(reg16_t.SP) + self.emu.push16(self.emu.get_gpreg(reg16_t.AX)) + self.emu.push16(self.emu.get_gpreg(reg16_t.CX)) + self.emu.push16(self.emu.get_gpreg(reg16_t.DX)) + self.emu.push16(self.emu.get_gpreg(reg16_t.BX)) + self.emu.push16(sp) + self.emu.push16(self.emu.get_gpreg(reg16_t.BP)) + self.emu.push16(self.emu.get_gpreg(reg16_t.SI)) + self.emu.push16(self.emu.get_gpreg(reg16_t.DI)) + + def popa(self): + self.emu.set_gpreg(reg16_t.DI, self.emu.pop16()) + self.emu.set_gpreg(reg16_t.SI, self.emu.pop16()) + self.emu.set_gpreg(reg16_t.BP, self.emu.pop16()) + sp = self.emu.pop16() + self.emu.set_gpreg(reg16_t.BX, self.emu.pop16()) + self.emu.set_gpreg(reg16_t.DX, self.emu.pop16()) + self.emu.set_gpreg(reg16_t.CX, self.emu.pop16()) + self.emu.set_gpreg(reg16_t.AX, self.emu.pop16()) + self.emu.set_gpreg(reg16_t.SP, sp) + + def push_imm16(self): + self.emu.push16(self.emu.constant(self.instr.imm16, Type.int_16)) + + def imul_r16_rm16_imm16(self): + rm16_s = self.get_rm16() + self.set_r16(rm16_s * self.instr.imm16) + self.emu.update_eflags_imul(rm16_s, self.instr.imm16) + + def push_imm8(self): + self.emu.push16(self.instr.imm8) + + def imul_r16_rm16_imm8(self): + rm16_s = self.get_rm16() + self.set_r16(rm16_s * self.instr.imm8) + self.emu.update_eflags_imul(rm16_s, self.instr.imm8) + + def test_rm16_r16(self): + rm16 = self.get_rm16() + r16 = self.get_r16() + self.emu.update_eflags_and(rm16, r16) + + def xchg_r16_rm16(self): + r16 = self.get_r16() + rm16 = self.get_rm16() + self.set_r16(rm16) + self.set_rm16(r16) + + def mov_rm16_r16(self): + r16 = self.get_r16() + self.set_rm16(r16) + + def mov_r16_rm16(self): + rm16 = self.get_rm16() + self.set_r16(rm16) + + def mov_rm16_sreg(self): + sreg = self.get_sreg() + self.set_rm16(sreg) + + def lea_r16_m16(self): + m16 = self.get_m() + self.set_r16(m16) + + def les_es_r16_m16(self): + m16 = self.get_m() + self.set_r16(m16) + + def xchg_r16_ax(self): + reg = self.instr.opcode & 0b111 + r16 = self.emu.get_gpreg(reg16_t(reg)) + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t(reg), ax) + self.emu.set_gpreg(reg16_t.AX, r16) + + def cbw(self): + al_s = self.emu.get_gpreg(reg8_t.AL).widen_signed(Type.int_16) + self.emu.set_gpreg(reg16_t.AX, al_s) + + def cwd(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t.DX, -1 if ax & 0x8000 else 0) + + def callf_ptr16_16(self): + self.emu.callf(self.instr.ptr16, self.instr.imm16) + + + def pushf(self): + self.emu.push16(self.emu.get_flags()) + + def popf(self): + self.emu.set_flags(self.emu.pop16()) + + def mov_ax_moffs16(self): + self.emu.set_gpreg(reg16_t.AX, self.get_moffs16()) + + def mov_moffs16_ax(self): + self.set_moffs16(self.emu.get_gpreg(reg16_t.AX)) + + def cmps_m8_m8(self): + while True: + m8_s = self.emu.get_data8(sgreg_t(self.instr.segment), self.emu.get_gpreg(reg16_t.SI)) + m8_d = self.emu.get_data8(self.emu.ES, self.emu.get_gpreg(reg16_t.DI)) + self.emu.update_eflags_sub(m8_s, m8_d) + + self.emu.update_gpreg(reg16_t.SI, -1 if self.emu.is_direction() else 1) + self.emu.update_gpreg(reg16_t.DI, -1 if self.emu.is_direction() else 1) + + if self.instr.pre_repeat != self.emu.NONE: + self.emu.update_gpreg(reg16_t.CX, -1) + if self.instr.pre_repeat == self.emu.REPZ: + if not self.emu.get_gpreg(reg16_t.CX) or not self.emu.is_zero(): + break + elif self.instr.pre_repeat == self.emu.REPNZ: + if not self.emu.get_gpreg(reg16_t.CX) or self.emu.is_zero(): + break + else: + break + + def cmps_m16_m16(self): + while True: + m16_s = self.emu.get_data16(sgreg_t(self.instr.segment), self.emu.get_gpreg(reg16_t.SI)) + m16_d = self.emu.get_data16(reg16_t.ES, self.emu.get_gpreg(reg16_t.DI)) + self.emu.update_eflags_sub(m16_s, m16_d) + + self.emu.update_gpreg(reg16_t.SI, -2 if self.emu.is_direction() else 2) + self.emu.update_gpreg(reg16_t.DI, -2 if self.emu.is_direction() else 2) + + if self.instr.pre_repeat != self.emu.NONE: + self.emu.update_gpreg(reg16_t.CX, -1) + if self.instr["pre_ repeat"] == self.emu.REPZ: + if not self.emu.get_gpreg(reg16_t.CX) or not self.emu.is_zero(): + break + elif self.instr.pre_repeat == self.emu.REPNZ: + if not self.emu.get_gpreg(reg16_t.CX) or self.emu.is_zero(): + break + else: + break + + + def movsw_m16_m16(self): + if self.instr.pre_repeat != NONE: + self.emu.update_gpreg(reg16_t.CX, -1) + ip = self.emu.get_gpreg(reg16_t.IP) + if self.instr.pre_repeat == REPZ: + self.emu.lifter_instruction.jump(self.emu.get_gpreg(reg16_t.CX) == 0, ip, JumpKind.Boring) + elif self.instr.pre_repeat == REPNZ: + self.emu.lifter_instruction.jump(self.emu.get_gpreg(reg16_t.CX) != 0, ip, JumpKind.Boring) + + m16_s = self.emu.get_data16(sgreg_t(self.instr.segment), self.emu.get_gpreg(reg16_t.SI)) + self.emu.put_data16(sgreg_t.ES, self.emu.get_gpreg(reg16_t.DI), m16_s) + + self.emu.update_gpreg(reg16_t.SI, 2 if ~self.emu.is_direction() else -2) + self.emu.update_gpreg(reg16_t.DI, 2 if ~self.emu.is_direction() else -2) + + + def test_ax_imm16(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.update_eflags_and(ax, self.instr.imm16) + + def mov_r16_imm16(self): + reg = self.instr.opcode & 0b111 + self.emu.set_gpreg(reg16_t(reg), self.instr.imm16) + + def ret(self): + ip = self.emu.pop16() + self.emu.lifter_instruction.jump(None, ip, jumpkind=JumpKind.Ret) + + def mov_rm16_imm16(self): + self.set_rm16(self.emu.constant(self.instr.imm16, Type.int_16)) + + def leave(self): + ebp = self.emu.get_gpreg(reg16_t.BP) + self.emu.set_gpreg(reg16_t.SP, ebp) + self.emu.set_gpreg(reg16_t.BP, self.emu.pop16()) + + def in_ax_imm8(self): + self.emu.set_gpreg(reg16_t.AX, self.emu.in_io16(self.instr.imm8)) + + def out_imm8_ax(self): + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.out_io16(self.instr.imm8, ax) + + def call_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.push16(ip + 3) + self.emu.lifter_instruction.jump(None, ip + self.emu.constant(self.instr.imm16, Type.int_16).signed + 3, jumpkind=JumpKind.Call) + + + def jmp_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 3 + self.emu.lifter_instruction.jump(None, ip, JumpKind.Boring) + + def jmpf_ptr16_16(self): + self.emu.jmpf(self.instr.ptr16, self.instr.imm16) + + def in_ax_dx(self): + dx = self.emu.get_gpreg(reg16_t.DX) + self.emu.set_gpreg(reg16_t.AX, self.emu.in_io16(dx)) + + def out_dx_ax(self): + dx = self.emu.get_gpreg(reg16_t.DX) + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.out_io16(dx, ax) + + def jo_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not self.emu.is_overflow(), ip) + + def jno_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_overflow(), ip) + + def jb_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not self.emu.is_carry(), ip) + + def jnb_rel16(self): # jae, jnc + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_carry(), ip) + + def jz_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not self.emu.is_zero(), ip) + + def jnz_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_zero(), ip) + + def jbe_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not (self.emu.is_carry() or self.emu.is_zero()), ip) + + def ja_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_carry() or self.emu.is_zero(), ip) + + def js_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not self.emu.is_sign(), ip) + + def jns_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_sign(), ip) + + def jp_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not self.emu.is_parity(), ip) + + def jnp_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_parity(), ip) + + def jl_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not (self.emu.is_sign() != self.emu.is_overflow()), ip) + + def jnl_rel16(self): # jge + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_sign() != self.emu.is_overflow(), ip) + + def jle_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(not (self.emu.is_zero() or (self.emu.is_sign() != self.emu.is_overflow())), ip) + + def jnle_rel16(self): + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm16, Type.int_16).signed + 4 + self.emu.lifter_instruction.jump(self.emu.is_zero() or (self.emu.is_sign() != self.emu.is_overflow()), + ip) + + def imul_r16_rm16(self): + r16_s = self.get_r16() + rm16_s = self.get_rm16() + self.set_r16(r16_s * rm16_s) + self.emu.update_eflags_imul(r16_s, rm16_s) + + def movzx_r16_rm8(self): + rm8 = self.emu.get_data8(sgreg_t(self.instr.segment), self.calc_modrm()) + self.set_r16(rm8) + + def movzx_r16_rm16(self): + rm16 = self.get_rm16() + self.set_r16(rm16) + + def movsx_r16_rm8(self): + rm8_s = self.emu.get_data8(sgreg_t(self.instr.segment), self.calc_modrm()).widen_signed(Type.int_16) + self.set_r16(rm8_s) + + def movsx_r16_rm16(self): + rm16_s = self.get_rm16().signed # TODO source is 16 bit?? + self.set_r16(rm16_s) + + def code_81(self): + reg = self.instr.modrm.reg + if reg == 0: + self.add_rm16_imm16() + elif reg == 1: + self.or_rm16_imm16() + elif reg == 2: + self.adc_rm16_imm16() + elif reg == 3: + self.sbb_rm16_imm16() + elif reg == 4: + self.and_rm16_imm16() + elif reg == 5: + self.sub_rm16_imm16() + elif reg == 6: + self.xor_rm16_imm16() + elif reg == 7: + self.cmp_rm16_imm16() + else: + raise RuntimeError(f"not implemented: 0x81 /{reg}") + + def code_83(self): + reg = self.instr.modrm.reg + if reg == 0: + self.add_rm16_imm8() + elif reg == 1: + self.or_rm16_imm8() + elif reg == 2: + self.adc_rm16_imm8() + elif reg == 3: + self.sbb_rm16_imm8() + elif reg == 4: + self.and_rm16_imm8() + elif reg == 5: + self.sub_rm16_imm8() + elif reg == 6: + self.xor_rm16_imm8() + elif reg == 7: + self.cmp_rm16_imm8() + else: + raise RuntimeError(f"not implemented: 0x83 /{reg}") + + def code_c1(self): + reg = self.instr.modrm.reg + if reg == 4: + self.shl_rm16_imm8() + elif reg == 5: + self.shr_rm16_imm8() + elif reg == 6: + self.sal_rm16_imm8() + elif reg == 7: + self.sar_rm16_imm8() + else: + raise RuntimeError(f"not implemented: 0xc1 /{reg}") + + def code_d1(self): + reg = self.instr.modrm.reg + if reg == 4: + self.shl_rm16_1() + elif reg == 5: + self.shr_rm16_1() + elif reg == 6: + self.sal_rm16_1() + elif reg == 7: + self.sar_rm16_1() + else: + raise RuntimeError(f"not implemented: 0xd1 /{reg}") + + def code_d3(self): + reg = self.instr.modrm.reg + if reg == 0: + self.rol_rm16_cl() + elif reg == 4: + self.shl_rm16_cl() + elif reg == 5: + self.shr_rm16_cl() + elif reg == 6: + self.sal_rm16_cl() + elif reg == 7: + self.sar_rm16_cl() + else: + raise RuntimeError(f"not implemented: 0xd3 /{reg}") + + def code_f7(self): + reg = self.instr.modrm.reg + if reg == 0: + self.test_rm16_imm16() + elif reg == 2: + self.not_rm16() + elif reg == 3: + self.neg_rm16() + elif reg == 4: + self.mul_dx_ax_rm16() + elif reg == 5: + self.imul_dx_ax_rm16() + elif reg == 6: + self.div_dx_ax_rm16() + elif reg == 7: + self.idiv_dx_ax_rm16() + else: + raise RuntimeError(f"not implemented: 0xf7 /{reg}") + + def code_ff(self): + reg = self.instr.modrm.reg + if reg == 0: + self.inc_rm16() + elif reg == 1: + self.dec_rm16() + elif reg == 2: + self.call_rm16() + elif reg == 3: + self.callf_m16_16() + elif reg == 4: + self.jmp_rm16() + elif reg == 5: + self.jmpf_m16_16() + elif reg == 6: + self.push_rm16() + else: + raise RuntimeError(f"not implemented: 0xff /{reg}") + + def code_0f00(self): + reg = self.instr.modrm.reg + if reg == 3: + self.ltr_rm16() + else: + raise RuntimeError(f"not implemented: 0x0f00 /{reg}") + + def code_0f01(self): + reg = self.instr.modrm.reg + #if reg == 2: + # self.lgdt_m24() + #elif reg == 3: + # self.lidt_m24() + #else: + raise RuntimeError(f"not implemented: 0x0f01 /{reg}") + + def add_rm16_imm16(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 + self.instr.imm16) + self.emu.update_eflags_add(rm16, self.instr.imm16) + + def or_rm16_imm16(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 | self.instr.imm16) + self.emu.update_eflags_or(rm16, self.instr.imm16) + + def adc_rm16_imm16(self): + rm16 = self.get_rm16() + cf = self.emu.is_carry() + self.set_rm16(rm16 + self.instr.imm16 + cf) + self.emu.update_eflags_add(rm16, self.instr.imm16 + cf) + + def sbb_rm16_imm16(self): + rm16 = self.get_rm16() + cf = self.emu.is_carry() + self.set_rm16(rm16 - self.instr.imm16 - cf) + self.emu.update_eflags_sbb(rm16, self.instr.imm16, cf) + + def and_rm16_imm16(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 & self.instr.imm16) + self.emu.update_eflags_and(rm16, self.instr.imm16) + + def sub_rm16_imm16(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 - self.instr.imm16) + self.emu.update_eflags_sub(rm16, self.instr.imm16) + + def xor_rm16_imm16(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 ^ self.instr.imm16) + + def cmp_rm16_imm16(self): + rm16 = self.get_rm16() + self.emu.update_eflags_sub(rm16, self.instr.imm16) + + def add_rm16_imm8(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 + self.instr.imm8) + self.emu.update_eflags_add(rm16, self.instr.imm8) + + def or_rm16_imm8(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 | self.instr.imm8) + self.emu.update_eflags_or(rm16, self.instr.imm8) + + def adc_rm16_imm8(self): + rm16 = self.get_rm16() + cf = self.emu.is_carry() + self.set_rm16(rm16 + self.instr.imm8 + cf) + self.emu.update_eflags_add(rm16, self.instr.imm8 + cf) + + def sbb_rm16_imm8(self): + rm16 = self.get_rm16() + cf = self.emu.is_carry() + self.set_rm16(rm16 - self.instr.imm8 - cf) + self.emu.update_eflags_sbb(rm16, self.instr.imm8, cf) + + def and_rm16_imm8(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 & self.instr.imm8) + self.emu.update_eflags_and(rm16, self.emu.constant(self.instr.imm8, Type.int_16)) + + def sub_rm16_imm8(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 - self.instr.imm8) + self.emu.update_eflags_sub(rm16, self.emu.constant(self.instr.imm8, Type.int_16)) + + def xor_rm16_imm8(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 ^ self.instr.imm8) + + def cmp_rm16_imm8(self): + rm16 = self.get_rm16() + self.emu.update_eflags_sub(rm16, self.instr.imm8) + + def shl_rm16_imm8(self): + rm16 = self.get_rm16() + self.shl(rm16, self.instr.imm8) + + def shr_rm16_imm8(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 >> self.instr.imm8) + self.emu.update_eflags_shr(rm16, self.instr.imm8) + + def sal_rm16_imm8(self): + rm16_s = self.get_rm16().signed + self.set_rm16(rm16_s << self.instr.imm8) + + def sar_rm16_imm8(self): + rm16_s = self.get_rm16().signed + self.set_rm16(rm16_s.sar(self.instr.imm8)) + + def shl_rm16_1(self): + rm16 = self.get_rm16() + cl = self.emu.constant(1, Type.int_8) + self.shl(rm16, cl) + + def rol_rm16_cl(self): + rm16 = self.get_rm16() + cl = self.emu.get_gpreg(reg8_t.CL) + self.rol(rm16, cl) + + def rol(self, a, b): + self.set_rm16(a.rol(b)) + self.emu.update_eflags_shl(a, b) + + def shl_rm16_cl(self): + rm16 = self.get_rm16() + cl = self.emu.get_gpreg(reg8_t.CL) + self.shl(rm16, cl) + + def shl(self, a, b): + self.set_rm16(a << b) + self.emu.update_eflags_shl(a, b) + + def shr_rm16_cl(self): + rm16 = self.get_rm16() + cl = self.emu.get_gpreg(reg8_t.CL) + self.shr(rm16, cl) + + def shr_rm16_1(self): + rm16 = self.get_rm16() + cl = self.emu.constant(1, Type.int_8) + self.shr(rm16, cl) + + def shr(self, a, b): + self.set_rm16(a >> b) + self.emu.update_eflags_shr(a, b) + + def sal_rm16_1(self): + rm16_s = self.get_rm16().signed + cl = self.emu.constant(1, Type.int_8) + self.set_rm16(rm16_s << cl) + + def sar_rm16_1(self): + rm16_s = self.get_rm16() + cl = self.emu.constant(1, Type.int_8) + self.set_rm16(rm16_s.sar(cl)) + + def sal_rm16_cl(self): + rm16_s = self.get_rm16().signed + cl = self.emu.get_gpreg(reg8_t.CL) + self.set_rm16(rm16_s << cl) + + def sar_rm16_cl(self): + rm16_s = self.get_rm16() + cl = self.emu.get_gpreg(reg8_t.CL) + self.set_rm16(rm16_s.sar(cl)) + + def test_rm16_imm16(self): + rm16 = self.get_rm16() + imm16 = self.instr.modrm.imm16 # self.emu.get_code16(0) + #self.emu.update_eip(2) + self.emu.update_eflags_and(rm16, imm16) + + def not_rm16(self): + rm16 = self.get_rm16() + self.set_rm16(~rm16) + + def neg_rm16(self): + rm16_s = self.get_rm16().signed + self.set_rm16((rm16_s * -1).cast_to(Type.int_16)) + self.emu.update_eflags_neg(rm16_s) + + def mul_dx_ax_rm16(self): + rm16 = self.get_rm16() + ax = self.emu.get_gpreg(reg16_t.AX) + val = ax * rm16 + self.emu.set_gpreg(reg16_t.AX, val & 0xFFFF) + self.emu.set_gpreg(reg16_t.DX, (val >> 16) & 0xFFFF) + self.emu.update_eflags_mul(ax, rm16) + + def imul_dx_ax_rm16(self): + rm16_s = self.get_rm16().signed + ax_s = self.emu.get_gpreg(reg16_t.AX).signed + val_s = ax_s * rm16_s + self.emu.set_gpreg(reg16_t.AX, val_s.cast_to(Type.int_16)) + self.emu.set_gpreg(reg16_t.DX, (val_s >> 16).cast_to(Type.int_16)) + self.emu.update_eflags_imul(ax_s, rm16_s) + + def div_dx_ax_rm16(self): + rm16 = self.get_rm16() + if rm16 == 0: + raise Exception(self.emu.EXP_DE) + val = (self.emu.get_gpreg(reg16_t.DX) << 16) | self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg16_t.AX, val // rm16) + self.emu.set_gpreg(reg16_t.DX, val % rm16) + + def idiv_dx_ax_rm16(self): + rm16_s = self.get_rm16().cast_to(Type.int_32, signed=True) + #if rm16_s == 0: + # raise Exception(self.emu.EXP_DE) + val_s = ((self.emu.get_gpreg(reg16_t.DX).cast_to(Type.int_32, signed=True) << 16) + | self.emu.get_gpreg(reg16_t.AX).cast_to(Type.int_32)) + self.emu.set_gpreg(reg16_t.AX, val_s // rm16_s) + self.emu.set_gpreg(reg16_t.DX, val_s % rm16_s) + + def inc_rm16(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 + 1) + self.emu.update_eflags_add(rm16, 1) + + def dec_rm16(self): + rm16 = self.get_rm16() + self.set_rm16(rm16 - 1) + self.emu.update_eflags_dec(rm16) + + def call_rm16(self): + rm16 = self.get_rm16() + self.emu.push16(self.emu.get_ip()) + self.emu.set_ip(rm16) + + def callf_m16_16(self): + m32 = self.get_m() + ip = self.emu.read_mem16(m32) # TODO: check segemnt, probably self.emu.get_data16(select_segment(), + cs = self.emu.read_mem16(m32 + 2) + self.emu.callf(cs, ip) + + def jmp_rm16(self): + rm16 = self.get_rm16() + self.emu.lifter_instruction.jump(None, rm16) + + def jmpf_m16_16(self): + m32 = self.get_m() + ip = self.emu.read_mem16(m32) + sel = self.emu.read_mem16(m32 + 2) + self.emu.jmpf(sel, ip) + + def push_rm16(self): + rm16 = self.get_rm16() + self.emu.push16(rm16) diff --git a/angr_platforms/X86_16/instr32.py b/angr_platforms/X86_16/instr32.py new file mode 100644 index 0000000..11ead7e --- /dev/null +++ b/angr_platforms/X86_16/instr32.py @@ -0,0 +1,866 @@ +import struct + +from pyvex.lifting.util import Type + +from .debug import ERROR, INFO +from .exception import EXCEPTION, EXP_DE +from .instr_base import InstrBase +from .instruction import * +from .regs import reg8_t, reg16_t, reg32_t + + +class Instr32(InstrBase): + + def __init__(self, emu, instr): + super().__init__(emu, instr, mode32=True) # X86Instruction + + self.set_funcflag(0x01, self.add_rm32_r32, CHK_MODRM) + self.set_funcflag(0x03, self.add_r32_rm32, CHK_MODRM) + self.set_funcflag(0x05, self.add_eax_imm32, CHK_IMM32) + self.set_funcflag(0x06, self.push_es, 0) + self.set_funcflag(0x07, self.pop_es, 0) + self.set_funcflag(0x09, self.or_rm32_r32, CHK_MODRM) + self.set_funcflag(0x0B, self.or_r32_rm32, CHK_MODRM) + self.set_funcflag(0x0D, self.or_eax_imm32, CHK_IMM32) + self.set_funcflag(0x11, self.adc_rm32_r32, CHK_MODRM) + self.set_funcflag(0x13, self.adc_r32_rm32, CHK_MODRM) + self.set_funcflag(0x16, self.push_ss, 0) + self.set_funcflag(0x17, self.pop_ss, 0) + self.set_funcflag(0x1E, self.push_ds, 0) + self.set_funcflag(0x1F, self.pop_ds, 0) + self.set_funcflag(0x21, self.and_rm32_r32, CHK_MODRM) + self.set_funcflag(0x23, self.and_r32_rm32, CHK_MODRM) + self.set_funcflag(0x25, self.and_eax_imm32, CHK_IMM32) + self.set_funcflag(0x29, self.sub_rm32_r32, CHK_MODRM) + self.set_funcflag(0x2B, self.sub_r32_rm32, CHK_MODRM) + self.set_funcflag(0x2D, self.sub_eax_imm32, CHK_IMM32) + self.set_funcflag(0x31, self.xor_rm32_r32, CHK_MODRM) + self.set_funcflag(0x33, self.xor_r32_rm32, CHK_MODRM) + self.set_funcflag(0x35, self.xor_eax_imm32, CHK_IMM32) + self.set_funcflag(0x39, self.cmp_rm32_r32, CHK_MODRM) + self.set_funcflag(0x3B, self.cmp_r32_rm32, CHK_MODRM) + self.set_funcflag(0x3D, self.cmp_eax_imm32, CHK_IMM32) + + for i in range(8): + self.set_funcflag(0x40 + i, self.inc_r32, 0) + self.set_funcflag(0x48 + i, self.dec_r32, 0) + self.set_funcflag(0x50 + i, self.push_r32, 0) + self.set_funcflag(0x58 + i, self.pop_r32, 0) + + self.set_funcflag(0x60, self.pushad, 0) + self.set_funcflag(0x61, self.popad, 0) + self.set_funcflag(0x68, self.push_imm32, CHK_IMM32) + self.set_funcflag(0x69, self.imul_r32_rm32_imm32, CHK_MODRM | CHK_IMM32) + self.set_funcflag(0x6A, self.push_imm8, CHK_IMM8) + self.set_funcflag(0x6B, self.imul_r32_rm32_imm8, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0x85, self.test_rm32_r32, CHK_MODRM) + self.set_funcflag(0x87, self.xchg_r32_rm32, CHK_MODRM) + self.set_funcflag(0x89, self.mov_rm32_r32, CHK_MODRM) + self.set_funcflag(0x8B, self.mov_r32_rm32, CHK_MODRM) + self.set_funcflag(0x8C, self.mov_rm32_sreg, CHK_MODRM) + self.set_funcflag(0x8D, self.lea_r32_m32, CHK_MODRM) + + for i in range(1, 8): + self.set_funcflag(0x90 + i, self.xchg_r32_eax, CHK_IMM32) + + self.set_funcflag(0x98, self.cwde, 0) + self.set_funcflag(0x99, self.cdq, 0) + self.set_funcflag(0x9A, self.callf_ptr16_32, CHK_PTR16 | CHK_IMM32) + self.set_funcflag(0x9C, self.pushf, 0) + self.set_funcflag(0x9D, self.popf, 0) + self.set_funcflag(0xA1, self.mov_eax_moffs32, CHK_MOFFS) + self.set_funcflag(0xA3, self.mov_moffs32_eax, CHK_MOFFS) + self.set_funcflag(0xA6, self.cmps_m8_m8, 0) + self.set_funcflag(0xA7, self.cmps_m32_m32, 0) + self.set_funcflag(0xA9, self.test_eax_imm32, CHK_IMM32) + + for i in range(8): + self.set_funcflag(0xB8 + i, self.mov_r32_imm32, CHK_IMM32) + + self.set_funcflag(0xC3, self.ret, 0) + self.set_funcflag(0xC7, self.mov_rm32_imm32, CHK_MODRM | CHK_IMM32) + self.set_funcflag(0xC9, self.leave, 0) + self.set_funcflag(0xE5, self.in_eax_imm8, CHK_IMM8) + self.set_funcflag(0xE7, self.out_imm8_eax, CHK_IMM8) + self.set_funcflag(0xE8, self.call_rel32, CHK_IMM32) + self.set_funcflag(0xE9, self.jmp_rel32, CHK_IMM32) + self.set_funcflag(0xEA, self.jmpf_ptr16_32, CHK_PTR16 | CHK_IMM32) + self.set_funcflag(0xED, self.in_eax_dx, 0) + self.set_funcflag(0xEF, self.out_dx_eax, 0) + + self.set_funcflag(0x0F80, self.jo_rel32, CHK_IMM32) + self.set_funcflag(0x0F81, self.jno_rel32, CHK_IMM32) + self.set_funcflag(0x0F82, self.jb_rel32, CHK_IMM32) + self.set_funcflag(0x0F83, self.jnb_rel32, CHK_IMM32) + self.set_funcflag(0x0F84, self.jz_rel32, CHK_IMM32) + self.set_funcflag(0x0F85, self.jnz_rel32, CHK_IMM32) + self.set_funcflag(0x0F86, self.jbe_rel32, CHK_IMM32) + self.set_funcflag(0x0F87, self.ja_rel32, CHK_IMM32) + self.set_funcflag(0x0F88, self.js_rel32, CHK_IMM32) + self.set_funcflag(0x0F89, self.jns_rel32, CHK_IMM32) + self.set_funcflag(0x0F8A, self.jp_rel32, CHK_IMM32) + self.set_funcflag(0x0F8B, self.jnp_rel32, CHK_IMM32) + self.set_funcflag(0x0F8C, self.jl_rel32, CHK_IMM32) + self.set_funcflag(0x0F8D, self.jnl_rel32, CHK_IMM32) + self.set_funcflag(0x0F8E, self.jle_rel32, CHK_IMM32) + self.set_funcflag(0x0F8F, self.jnle_rel32, CHK_IMM32) + + self.set_funcflag(0x0FAF, self.imul_r32_rm32, CHK_MODRM) + self.set_funcflag(0x0FB6, self.movzx_r32_rm8, CHK_MODRM) + self.set_funcflag(0x0FB7, self.movzx_r32_rm16, CHK_MODRM) + self.set_funcflag(0x0FBE, self.movsx_r32_rm8, CHK_MODRM) + self.set_funcflag(0x0FBF, self.movsx_r32_rm16, CHK_MODRM) + + self.set_funcflag(0x81, self.code_81, CHK_MODRM | CHK_IMM32) + self.set_funcflag(0x83, self.code_83, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0xC1, self.code_c1, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0xD3, self.code_d3, CHK_MODRM) + self.set_funcflag(0xF7, self.code_f7, CHK_MODRM) + self.set_funcflag(0xFF, self.code_ff, CHK_MODRM) + self.set_funcflag(0x0F00, self.code_0f00, CHK_MODRM) + self.set_funcflag(0x0F01, self.code_0f01, CHK_MODRM) + + def add_rm32_r32(self): + rm32 = self.get_rm32() + r32 = self.get_r32() + self.set_rm32(rm32 + r32) + self.emu.update_eflags_add(rm32, r32) + + + def adc_rm32_r32(self) -> None: + rm32 = self.get_rm32() + r32 = self.get_r32() + carry = self.emu.is_carry().cast_to(Type.int_32) + self.set_rm8(rm32 + r32 + carry) + self.emu.update_eflags_adc(rm32, r32, carry) + + def add_r32_rm32(self): + r32 = self.get_r32() + rm32 = self.get_rm32() + self.set_r32(r32 + rm32) + self.emu.update_eflags_add(r32, rm32) + + def adc_r32_rm32(self) -> None: + r32 = self.get_r32() + rm32 = self.get_rm32() + carry = self.emu.is_carry().cast_to(Type.int_32) + self.set_r32(r32 + rm32 + carry) + self.emu.update_eflags_adc(r32, rm32, carry) + + def add_eax_imm32(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.set_gpreg(reg32_t.EAX, eax + self.instr.imm32) + self.emu.update_eflags_add(eax, self.instr.imm32) + + def push_es(self): + self.emu.push32(self.emu.get_segment(reg16_t.ES)) + + def pop_es(self): + self.emu.set_segment(reg16_t.ES, self.emu.pop32()) + + def or_rm32_r32(self): + rm32 = self.get_rm32() + r32 = self.get_r32() + self.set_rm32(rm32 | r32) + self.emu.update_eflags_or(rm32, r32) + + def or_r32_rm32(self): + r32 = self.get_r32() + rm32 = self.get_rm32() + self.set_r32(r32 | rm32) + self.emu.update_eflags_or(r32, rm32) + + def or_eax_imm32(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.set_gpreg(reg32_t.EAX, eax | self.instr.imm32) + self.emu.update_eflags_or(eax, self.instr.imm32) + + def push_ss(self): + self.emu.push32(self.emu.get_segment(reg16_t.SS)) + + def pop_ss(self): + self.emu.set_segment(reg16_t.SS, self.emu.pop32()) + + def push_ds(self): + self.emu.push32(self.emu.get_segment(reg16_t.DS)) + + def pop_ds(self): + self.emu.set_segment(reg16_t.DS, self.emu.pop32()) + + def and_rm32_r32(self): + rm32 = self.get_rm32() + r32 = self.get_r32() + self.set_rm32(rm32 & r32) + self.emu.update_eflags_and(rm32, r32) + + def and_r32_rm32(self): + r32 = self.get_r32() + rm32 = self.get_rm32() + self.set_r32(r32 & rm32) + self.emu.update_eflags_and(r32, rm32) + + def and_eax_imm32(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.set_gpreg(reg32_t.EAX, eax & self.instr.imm32) + self.emu.update_eflags_and(eax, self.instr.imm32) + + def sub_rm32_r32(self): + rm32 = self.get_rm32() + r32 = self.get_r32() + self.set_rm32(rm32 - r32) + self.emu.update_eflags_sub(rm32, r32) + + def sub_r32_rm32(self): + r32 = self.get_r32() + rm32 = self.get_rm32() + self.set_r32(r32 - rm32) + self.emu.update_eflags_sub(r32, rm32) + + def sub_eax_imm32(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.set_gpreg(reg32_t.EAX, eax - self.instr.imm32) + self.emu.update_eflags_sub(eax, self.instr.imm32) + + def xor_rm32_r32(self): + rm32 = self.get_rm32() + r32 = self.get_r32() + self.set_rm32(rm32 ^ r32) + + def xor_r32_rm32(self): + r32 = self.get_r32() + rm32 = self.get_rm32() + self.set_r32(r32 ^ rm32) + + def xor_eax_imm32(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.set_gpreg(reg32_t.EAX, eax ^ self.instr.imm32) + + def cmp_rm32_r32(self): + rm32 = self.get_rm32() + r32 = self.get_r32() + self.emu.update_eflags_sub(rm32, r32) + + def cmp_r32_rm32(self): + r32 = self.get_r32() + rm32 = self.get_rm32() + self.emu.update_eflags_sub(r32, rm32) + + def cmp_eax_imm32(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.update_eflags_sub(eax, self.instr.imm32) + + def inc_r32(self): + reg = self.instr.opcode & ((1 << 3) - 1) + r32 = self.emu.get_gpreg(reg) + self.emu.set_gpreg(reg, r32 + 1) + self.emu.update_eflags_add(r32, 1) + + def dec_r32(self): + reg = self.instr.opcode & ((1 << 3) - 1) + r32 = self.emu.get_gpreg(reg) + self.emu.set_gpreg(reg, r32 - 1) + self.emu.update_eflags_sub(r32, 1) + + def push_r32(self): + reg = self.instr.opcode & ((1 << 3) - 1) + self.emu.push32(self.emu.get_gpreg(reg)) + + def pop_r32(self): + reg = self.instr.opcode & ((1 << 3) - 1) + self.emu.set_gpreg(reg, self.emu.pop32()) + + def pushad(self): + esp = self.emu.get_gpreg(reg32_t.ESP) + self.emu.push32(self.emu.get_gpreg(reg32_t.EAX)) + self.emu.push32(self.emu.get_gpreg(reg32_t.ECX)) + self.emu.push32(self.emu.get_gpreg(reg32_t.EDX)) + self.emu.push32(self.emu.get_gpreg(reg32_t.EBX)) + self.emu.push32(esp) + self.emu.push32(self.emu.get_gpreg(reg32_t.EBP)) + self.emu.push32(self.emu.get_gpreg(reg32_t.ESI)) + self.emu.push32(self.emu.get_gpreg(reg32_t.EDI)) + + def popad(self): + self.emu.set_gpreg(reg32_t.EDI, self.emu.pop32()) + self.emu.set_gpreg(reg32_t.ESI, self.emu.pop32()) + self.emu.set_gpreg(reg32_t.EBP, self.emu.pop32()) + esp = self.emu.pop32() + self.emu.set_gpreg(reg32_t.EBX, self.emu.pop32()) + self.emu.set_gpreg(reg32_t.EDX, self.emu.pop32()) + self.emu.set_gpreg(reg32_t.ECX, self.emu.pop32()) + self.emu.set_gpreg(reg32_t.EAX, self.emu.pop32()) + self.emu.set_gpreg(reg32_t.ESP, esp) + + def push_imm32(self): + self.emu.push32(self.instr.imm32) + + def imul_r32_rm32_imm32(self): + rm32_s = self.get_rm32() + self.set_r32(rm32_s * self.instr.imm32) + self.emu.update_eflags_imul(rm32_s, self.instr.imm32) + + def push_imm8(self): + self.emu.push32(self.instr.imm8) + + def imul_r32_rm32_imm8(self): + rm32_s = self.get_rm32() + self.set_r32(rm32_s * self.instr.imm8) + self.emu.update_eflags_imul(rm32_s, self.instr.imm8) + + def test_rm32_r32(self): + rm32 = self.get_rm32() + r32 = self.get_r32() + self.emu.update_eflags_and(rm32, r32) + + def xchg_r32_rm32(self): + r32 = self.get_r32() + rm32 = self.get_rm32() + self.set_r32(rm32) + self.set_rm32(r32) + + def mov_rm32_r32(self): + r32 = self.get_r32() + self.set_rm32(r32) + + def mov_r32_rm32(self): + rm32 = self.get_rm32() + self.set_r32(rm32) + + def mov_rm32_sreg(self): + sreg = self.get_sreg() + self.set_rm32(sreg) + + def lea_r32_m32(self): + m32 = self.get_m() + self.set_r32(m32) + + def xchg_r32_eax(self): + r32 = self.get_r32() + eax = self.emu.get_gpreg(reg32_t.EAX) + self.set_r32(eax) + self.emu.set_gpreg(reg32_t.EAX, r32) + + def cwde(self): + ax_s = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg32_t.EAX, ax_s) + + def cdq(self): + eax = self.emu.get_gpreg(reg32_t.EAX).signed + self.emu.set_gpreg(reg32_t.EDX, eax.sar(self.emu.constant(31, Type.int_8))) + + def callf_ptr16_32(self): + self.emu.callf(self.instr.ptr16, self.instr.imm32) + + def pushf(self): + self.emu.push32(self.emu.get_eflags()) + + def popf(self): + self.emu.set_eflags(self.emu.pop32()) + + def mov_eax_moffs32(self): + self.emu.set_gpreg(reg32_t.EAX, self.get_moffs32()) + + def mov_moffs32_eax(self): + self.set_moffs32(self.emu.get_gpreg(reg32_t.EAX)) + + def cmps_m8_m8(self): + while True: + m8_s = self.emu.get_data8( + self.select_segment(), self.emu.get_gpreg(reg32_t.ESI), + ) + m8_d = self.emu.get_data8(reg16_t.ES, self.emu.get_gpreg(reg32_t.EDI)) + self.emu.update_eflags_sub(m8_s, m8_d) + + self.emu.update_gpreg(reg32_t.ESI, -1 if self.emu.is_direction() else 1) + self.emu.update_gpreg(reg32_t.EDI, -1 if self.emu.is_direction() else 1) + + if self.instr.pre_repeat: + self.emu.update_gpreg(reg32_t.ECX, -1) + if self.instr.pre_repeat == REPZ: + if not self.emu.get_gpreg(reg32_t.ECX) or not self.emu.is_zero(): + break + elif self.instr.pre_repeat == REPNZ: + if not self.emu.get_gpreg(reg32_t.ECX) or self.emu.is_zero(): + break + else: + break + + def cmps_m32_m32(self): + while True: + m32_s = self.emu.get_data32( + self.select_segment(), self.emu.get_gpreg(reg32_t.ESI), + ) + m32_d = self.emu.get_data32(reg16_t.ES, self.emu.get_gpreg(reg32_t.EDI)) + self.emu.update_eflags_sub(m32_s, m32_d) + + self.emu.update_gpreg(reg32_t.ESI, -1 if self.emu.is_direction() else 1) + self.emu.update_gpreg(reg32_t.EDI, -1 if self.emu.is_direction() else 1) + + if self.instr.pre_repeat: + self.emu.update_gpreg(reg32_t.ECX, -1) + if self.instr.pre_repeat == REPZ: + if not self.emu.get_gpreg(reg32_t.ECX) or not self.emu.is_zero(): + break + elif self.instr.pre_repeat == REPNZ: + if not self.emu.get_gpreg(reg32_t.ECX) or self.emu.is_zero(): + break + else: + break + + def test_eax_imm32(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.update_eflags_and(eax, self.instr.imm32) + + def mov_r32_imm32(self): + reg = self.instr.opcode & ((1 << 3) - 1) + self.emu.set_gpreg(reg32_t(reg), self.instr.imm32) + + def ret(self): + self.emu.set_eip(self.emu.pop32()) + + def mov_rm32_imm32(self): + self.set_rm32(self.instr.imm32) + + def leave(self): + ebp = self.emu.get_gpreg(reg32_t.EBP) + self.emu.set_gpreg(reg32_t.ESP, ebp) + self.emu.set_gpreg(reg32_t.EBP, self.emu.pop32()) + + def in_eax_imm8(self): + self.emu.set_gpreg(reg32_t.EAX, self.emu.in_io32(self.instr.imm8)) + + def out_imm8_eax(self): + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.out_io32(self.instr.imm8, eax) + + def call_rel32(self): + self.emu.push32(self.emu.get_eip()) + self.emu.update_eip(self.instr.imm32) + + def jmp_rel32(self): + self.emu.update_eip(self.instr.imm32) + + def jmpf_ptr16_32(self): + self.emu.jmpf(self.instr.ptr16, self.instr.imm32) + + def in_eax_dx(self): + dx = self.emu.get_gpreg(reg16_t.DX) + self.emu.set_gpreg(reg32_t.EAX, self.emu.in_io32(dx)) + + def out_dx_eax(self): + dx = self.emu.get_gpreg(reg16_t.DX) + eax = self.emu.get_gpreg(reg32_t.EAX) + self.emu.out_io32(dx, eax) + + def jo_rel32(self): + if self.emu.is_overflow(): + self.emu.update_eip(self.instr.imm32) + + def jno_rel32(self): + ip = self.emu.get_gpreg(reg16_t.IP).cast_to(Type.int_32) + self.emu.constant(self.instr.imm32, Type.int_32).signed + 6 + self.emu.lifter_instruction.jump(self.emu.is_overflow(), ip) + + def jb_rel32(self): + if self.emu.is_carry(): + self.emu.update_eip(self.instr.imm32) + + def jnb_rel32(self): + if not self.emu.is_carry(): + self.emu.update_eip(self.instr.imm32) + + def jz_rel32(self): + if self.emu.is_zero(): + self.emu.update_eip(self.instr.imm32) + + def jnz_rel32(self): + if not self.emu.is_zero(): + self.emu.update_eip(self.instr.imm32) + + def jbe_rel32(self): + if self.emu.is_carry() or self.emu.is_zero(): + self.emu.update_eip(self.instr.imm32) + + def ja_rel32(self): + if not (self.emu.is_carry() or self.emu.is_zero()): + self.emu.update_eip(self.instr.imm32) + + def js_rel32(self): + if self.emu.is_sign(): + self.emu.update_eip(self.instr.imm32) + + def jns_rel32(self): + if not self.emu.is_sign(): + self.emu.update_eip(self.instr.imm32) + + def jp_rel32(self): + if self.emu.is_parity(): + self.emu.update_eip(self.instr.imm32) + + def jnp_rel32(self): + if not self.emu.is_parity(): + self.emu.update_eip(self.instr.imm32) + + def jl_rel32(self): + if self.emu.is_sign() != self.emu.is_overflow(): + self.emu.update_eip(self.instr.imm32) + + def jnl_rel32(self): + if self.emu.is_sign() == self.emu.is_overflow(): + self.emu.update_eip(self.instr.imm32) + + def jle_rel32(self): + if self.emu.is_zero() or ( + self.emu.is_sign() != self.emu.is_overflow() + ): + self.emu.update_eip(self.instr.imm32) + + def jnle_rel32(self): + if not self.emu.is_zero() and ( + self.emu.is_sign() == self.emu.is_overflow() + ): + self.emu.update_eip(self.instr.imm32) + + def imul_r32_rm32(self): + r32_s = self.get_r32() + rm32_s = self.get_rm32() + self.set_r32(r32_s * rm32_s) + self.emu.update_eflags_imul(r32_s, rm32_s) + + def movzx_r32_rm8(self): + rm8 = self.get_rm8() + self.set_r32(rm8) + + def movzx_r32_rm16(self): + rm16 = self.get_rm16() + self.set_r32(rm16) + + def movsx_r32_rm8(self): + rm8_s = self.get_rm8() + self.set_r32(rm8_s) + + def movsx_r32_rm16(self): + rm16_s = self.get_rm16() + self.set_r32(rm16_s) + + def code_81(self): + match self.instr.modrm.reg: + case 0: + self.add_rm32_imm32() + case 1: + self.or_rm32_imm32() + case 2: + self.adc_rm32_imm32() + case 3: + self.sbb_rm32_imm32() + case 4: + self.and_rm32_imm32() + case 5: + self.sub_rm32_imm32() + case 6: + self.xor_rm32_imm32() + case 7: + self.cmp_rm32_imm32() + case _: + ERROR("not implemented: 0x81 /%d\n", self.instr.modrm.reg) + + def code_83(self): + match self.instr.modrm.reg: + case 0: + self.add_rm32_imm8() + case 1: + self.or_rm32_imm8() + case 2: + self.adc_rm32_imm8() + case 3: + self.sbb_rm32_imm8() + case 4: + self.and_rm32_imm8() + case 5: + self.sub_rm32_imm8() + case 6: + self.xor_rm32_imm8() + case 7: + self.cmp_rm32_imm8() + case _: + ERROR("not implemented: 0x83 /%d\n", self.instr.modrm.reg) + + def code_c1(self): + match self.instr.modrm.reg: + case 4: + self.shl_rm32_imm8() + case 5: + self.shr_rm32_imm8() + case 6: + self.sal_rm32_imm8() + case 7: + self.sar_rm32_imm8() + case _: + ERROR("not implemented: 0xc1 /%d\n", self.instr.modrm.reg) + + def code_d3(self): + match self.instr.modrm.reg: + case 4: + self.shl_rm32_cl() + case 5: + self.shr_rm32_cl() + case 6: + self.sal_rm32_cl() + case 7: + self.sar_rm32_cl() + case _: + ERROR("not implemented: 0xd3 /%d\n", self.instr.modrm.reg) + + def code_f7(self): + match self.instr.modrm.reg: + case 0: + self.test_rm32_imm32() + case 2: + self.not_rm32() + case 3: + self.neg_rm32() + case 4: + self.mul_edx_eax_rm32() + case 5: + self.imul_edx_eax_rm32() + case 6: + self.div_edx_eax_rm32() + case 7: + self.idiv_edx_eax_rm32() + case _: + ERROR("not implemented: 0xf7 /%d\n", self.instr.modrm.reg) + + def code_ff(self): + match self.instr.modrm.reg: + case 0: + self.inc_rm32() + case 1: + self.dec_rm32() + case 2: + self.call_rm32() + case 3: + self.callf_m16_32() + case 4: + self.jmp_rm32() + case 5: + self.jmpf_m16_32() + case 6: + self.push_rm32() + case _: + ERROR("not implemented: 0xff /%d\n", self.instr.modrm.reg) + + def code_0f00(self): + match self.instr.modrm.reg: + case 3: + self.ltr_rm16() + case _: + ERROR("not implemented: 0x0f00 /%d\n", self.instr.modrm.reg) + + def code_0f01(self): + match self.instr.modrm.reg: + case 2: + self.lgdt_m32() + case 3: + self.lidt_m32() + case _: + ERROR("not implemented: 0x0f01 /%d\n", self.instr.modrm.reg) + + def add_rm32_imm32(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 + self.instr.imm32) + self.emu.update_eflags_add(rm32, self.instr.imm32) + + def or_rm32_imm32(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 | self.instr.imm32) + self.emu.update_eflags_or(rm32, self.instr.imm32) + + def adc_rm32_imm32(self): + rm32 = self.get_rm32() + cf = self.emu.is_carry() + self.set_rm32(rm32 + self.instr.imm32 + cf) + self.emu.update_eflags_add(rm32, self.instr.imm32 + cf) + + def sbb_rm32_imm32(self): + rm32 = self.get_rm32() + cf = self.emu.is_carry() + self.set_rm32(rm32 - self.instr.imm32 - cf) + self.emu.update_eflags_sub(rm32, self.instr.imm32 + cf) + + def and_rm32_imm32(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 & self.instr.imm32) + self.emu.update_eflags_and(rm32, self.instr.imm32) + + def sub_rm32_imm32(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 - self.instr.imm32) + self.emu.update_eflags_sub(rm32, self.instr.imm32) + + def xor_rm32_imm32(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 ^ self.instr.imm32) + + def cmp_rm32_imm32(self): + rm32 = self.get_rm32() + self.emu.update_eflags_sub(rm32, self.instr.imm32) + + def add_rm32_imm8(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 + self.instr.imm8) + self.emu.update_eflags_add(rm32, self.instr.imm8) + + def or_rm32_imm8(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 | self.instr.imm8) + self.emu.update_eflags_or(rm32, self.instr.imm8) + + def adc_rm32_imm8(self): + rm32 = self.get_rm32() + cf = self.emu.is_carry() + self.set_rm32(rm32 + self.instr.imm8 + cf) + self.emu.update_eflags_add(rm32, self.instr.imm8 + cf) + + def sbb_rm32_imm8(self): + rm32 = self.get_rm32() + cf = self.emu.is_carry() + self.set_rm32(rm32 - self.instr.imm8 - cf) + self.emu.update_eflags_sub(rm32, self.instr.imm8 + cf) + + def and_rm32_imm8(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 & self.instr.imm8) + self.emu.update_eflags_and(rm32, self.instr.imm8) + + def sub_rm32_imm8(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 - self.instr.imm8) + self.emu.update_eflags_sub(rm32, self.instr.imm8) + + def xor_rm32_imm8(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 ^ self.instr.imm8) + + def cmp_rm32_imm8(self): + rm32 = self.get_rm32() + self.emu.update_eflags_sub(rm32, self.instr.imm8) + + def shl_rm32_imm8(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 << self.instr.imm8) + self.emu.update_eflags_shl(rm32, self.instr.imm8) + + def shr_rm32_imm8(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 >> self.instr.imm8) + self.emu.update_eflags_shr(rm32, self.instr.imm8) + + def sal_rm32_imm8(self): + rm32_s = self.get_rm32() + self.set_rm32(rm32_s << self.instr.imm8) + + def sar_rm32_imm8(self): + rm32_s = self.get_rm32() + self.set_rm32(rm32_s >> self.instr.imm8) + + def shl_rm32_cl(self): + rm32 = self.get_rm32() + cl = self.emu.get_gpreg(reg8_t.CL) + self.set_rm32(rm32 << cl) + self.emu.update_eflags_shl(rm32, cl) + + def shr_rm32_cl(self): + rm32 = self.get_rm32() + cl = self.emu.get_gpreg(reg8_t.CL) + self.set_rm32(rm32 >> cl) + self.emu.update_eflags_shr(rm32, cl) + + def sal_rm32_cl(self): + rm32_s = self.get_rm32() + cl = self.emu.get_gpreg(reg8_t.CL) + self.set_rm32(rm32_s << cl) + + def sar_rm32_cl(self): + rm32_s = self.get_rm32() + cl = self.emu.get_gpreg(reg8_t.CL) + self.set_rm32(rm32_s >> cl) + + def test_rm32_imm32(self): + rm32 = self.get_rm32() + imm32 = struct.unpack("> 32) & 0xFFFFFFFF) + self.emu.update_eflags_mul(eax, rm32) + + def imul_edx_eax_rm32(self): + rm32_s = self.get_rm32() + eax_s = self.emu.get_gpreg(reg32_t.EAX) + val_s = eax_s * rm32_s + self.emu.set_gpreg(reg32_t.EAX, val_s & 0xFFFFFFFF) + self.emu.set_gpreg(reg32_t.EDX, (val_s >> 32) & 0xFFFFFFFF) + self.emu.update_eflags_imul(eax_s, rm32_s) + + def div_edx_eax_rm32(self): + rm32 = self.get_rm32() + EXCEPTION(EXP_DE, not rm32) + val = (self.emu.get_gpreg(reg32_t.EDX) << 32) | self.emu.get_gpreg(reg32_t.EAX) + self.emu.set_gpreg(reg32_t.EAX, val // rm32) + self.emu.set_gpreg(reg32_t.EDX, val % rm32) + + def idiv_edx_eax_rm32(self): + rm32_s = self.get_rm32() + EXCEPTION(EXP_DE, not rm32_s) + val_s = (self.emu.get_gpreg(reg32_t.EDX) << 32) | self.emu.get_gpreg(reg32_t.EAX) + self.emu.set_gpreg(reg32_t.EAX, val_s // rm32_s) + self.emu.set_gpreg(reg32_t.EDX, val_s % rm32_s) + + def inc_rm32(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 + 1) + self.emu.update_eflags_add(rm32, 1) + + def dec_rm32(self): + rm32 = self.get_rm32() + self.set_rm32(rm32 - 1) + self.emu.update_eflags_sub(rm32, 1) + + def call_rm32(self): + rm32 = self.get_rm32() + self.emu.push32(self.emu.get_eip()) + self.emu.set_eip(rm32) + + def callf_m16_32(self): + m48 = self.get_m() + eip = self.emu.read_mem32(m48) + cs = self.emu.read_mem16(m48 + 4) + INFO(2, "cs = 0x%04x, eip = 0x%08x", cs, eip) + self.emu.callf(cs, eip) + + def jmp_rm32(self): + rm32 = self.get_rm32() + self.emu.set_eip(rm32) + + def jmpf_m16_32(self): + m48 = self.get_m() + eip = self.emu.read_mem32(m48) + sel = self.emu.read_mem16(m48 + 4) + self.emu.jmpf(sel, eip) + + def push_rm32(self): + rm32 = self.get_rm32() + self.emu.push32(rm32) diff --git a/angr_platforms/X86_16/instr_base.py b/angr_platforms/X86_16/instr_base.py new file mode 100644 index 0000000..6f612e7 --- /dev/null +++ b/angr_platforms/X86_16/instr_base.py @@ -0,0 +1,714 @@ +from typing import TYPE_CHECKING, Any, Callable, Dict + +from pyvex.lifting.util import JumpKind +from pyvex.lifting.util.vex_helper import Type + +from .emu import EmuInstr +from .exec import ExecInstr +from .instruction import * +from .parse import ParseInstr +from .regs import reg8_t, reg16_t, sgreg_t + +if TYPE_CHECKING: + from .emulator import Emulator + +CHSZ_NONE: int = 0 +CHSZ_OP: int = 1 +CHSZ_AD: int = 2 + + +class InstrBase(ExecInstr, ParseInstr, EmuInstr): + def __init__(self, emu: Emulator, instr: InstrData, mode32: bool): + super().__init__(emu) + super(ExecInstr, self).__init__(emu, instr, mode32) # ParseInstr + super(ParseInstr, self).__init__(emu, instr, mode32) # EmuInstr + self.emu = emu + self.instrfuncs: Dict[int, Callable[[Dict[str, Any]], None]] = {} + self.chk: Dict[int, int] = {} + self.chsz_ad = False + + self.set_funcflag(0x00, self.add_rm8_r8, CHK_MODRM) + self.set_funcflag(0x02, self.add_r8_rm8, CHK_MODRM) + self.set_funcflag(0x04, self.add_al_imm8, CHK_IMM8) + self.set_funcflag(0x08, self.or_rm8_r8, CHK_MODRM) + self.set_funcflag(0x0A, self.or_r8_rm8, CHK_MODRM) + self.set_funcflag(0x0C, self.or_al_imm8, CHK_IMM8) + self.set_funcflag(0x10, self.adc_rm8_r8, CHK_MODRM) + self.set_funcflag(0x12, self.adc_r8_rm8, CHK_MODRM) + self.set_funcflag(0x20, self.and_rm8_r8, CHK_MODRM) + self.set_funcflag(0x22, self.and_r8_rm8, CHK_MODRM) + self.set_funcflag(0x24, self.and_al_imm8, CHK_IMM8) + self.set_funcflag(0x28, self.sub_rm8_r8, CHK_MODRM) + self.set_funcflag(0x2A, self.sub_r8_rm8, CHK_MODRM) + self.set_funcflag(0x2C, self.sub_al_imm8, CHK_IMM8) + self.set_funcflag(0x30, self.xor_rm8_r8, CHK_MODRM) + self.set_funcflag(0x32, self.xor_r8_rm8, CHK_MODRM) + self.set_funcflag(0x34, self.xor_al_imm8, CHK_IMM8) + self.set_funcflag(0x38, self.cmp_rm8_r8, CHK_MODRM) + self.set_funcflag(0x3A, self.cmp_r8_rm8, CHK_MODRM) + self.set_funcflag(0x3C, self.cmp_al_imm8, CHK_IMM8) + self.set_funcflag(0x70, self.jo_rel8, CHK_IMM8) + self.set_funcflag(0x71, self.jno_rel8, CHK_IMM8) + self.set_funcflag(0x72, self.jb_rel8, CHK_IMM8) + self.set_funcflag(0x73, self.jnb_rel8, CHK_IMM8) + self.set_funcflag(0x74, self.jz_rel8, CHK_IMM8) + self.set_funcflag(0x75, self.jnz_rel8, CHK_IMM8) + self.set_funcflag(0x76, self.jbe_rel8, CHK_IMM8) + self.set_funcflag(0x77, self.ja_rel8, CHK_IMM8) + self.set_funcflag(0x78, self.js_rel8, CHK_IMM8) + self.set_funcflag(0x79, self.jns_rel8, CHK_IMM8) + self.set_funcflag(0x7A, self.jp_rel8, CHK_IMM8) + self.set_funcflag(0x7B, self.jnp_rel8, CHK_IMM8) + self.set_funcflag(0x7C, self.jl_rel8, CHK_IMM8) + self.set_funcflag(0x7D, self.jnl_rel8, CHK_IMM8) + self.set_funcflag(0x7E, self.jle_rel8, CHK_IMM8) + self.set_funcflag(0x7F, self.jnle_rel8, CHK_IMM8) + self.set_funcflag(0x84, self.test_rm8_r8, CHK_MODRM) + self.set_funcflag(0x86, self.xchg_r8_rm8, CHK_MODRM) + self.set_funcflag(0x88, self.mov_rm8_r8, CHK_MODRM) + self.set_funcflag(0x8A, self.mov_r8_rm8, CHK_MODRM) + self.set_funcflag(0x8E, self.mov_sreg_rm16, CHK_MODRM) + self.set_funcflag(0x90, self.nop, 0) + self.set_funcflag(0x9F, self.lohf, 0) + self.set_funcflag(0xA0, self.mov_al_moffs8, CHK_MOFFS) + self.set_funcflag(0xA2, self.mov_moffs8_al, CHK_MOFFS) + self.set_funcflag(0xA8, self.test_al_imm8, CHK_IMM8) + for i in range(8): + self.set_funcflag(0xB0 + i, self.mov_r8_imm8, CHK_IMM8) + self.set_funcflag(0xC6, self.mov_rm8_imm8, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0xCA, self.retf_imm16, CHK_IMM16) + self.set_funcflag(0xCB, self.retf, 0) + self.set_funcflag(0xCC, self.int3, 0) + self.set_funcflag(0xCD, self.int_imm8, CHK_IMM8) + self.set_funcflag(0xCF, self.iret, 0) + self.set_funcflag(0xD0, self.code_d0_d2, CHK_MODRM) + self.set_funcflag(0xD2, self.code_d0_d2, CHK_MODRM) + self.set_funcflag(0xE4, self.in_al_imm8, CHK_IMM8) + self.set_funcflag(0xE6, self.out_imm8_al, CHK_IMM8) + self.set_funcflag(0xEB, self.jmp, CHK_IMM8) + self.set_funcflag(0xEC, self.in_al_dx, 0) + self.set_funcflag(0xEE, self.out_dx_al, 0) + self.set_funcflag(0xFA, self.cli, 0) + self.set_funcflag(0xFB, self.sti, 0) + self.set_funcflag(0xFC, self.cld, 0) + self.set_funcflag(0xFD, self.std, 0) + self.set_funcflag(0xF4, self.hlt, 0) + + self.set_funcflag(0x0F20, self.mov_r32_crn, CHK_MODRM) + self.set_funcflag(0x0F22, self.mov_crn_r32, CHK_MODRM) + self.set_funcflag(0x0F90, self.seto_rm8, CHK_MODRM) + self.set_funcflag(0x0F91, self.setno_rm8, CHK_MODRM) + self.set_funcflag(0x0F92, self.setb_rm8, CHK_MODRM) + self.set_funcflag(0x0F93, self.setnb_rm8, CHK_MODRM) + self.set_funcflag(0x0F94, self.setz_rm8, CHK_MODRM) + self.set_funcflag(0x0F95, self.setnz_rm8, CHK_MODRM) + self.set_funcflag(0x0F96, self.setbe_rm8, CHK_MODRM) + self.set_funcflag(0x0F97, self.seta_rm8, CHK_MODRM) + self.set_funcflag(0x0F98, self.sets_rm8, CHK_MODRM) + self.set_funcflag(0x0F99, self.setns_rm8, CHK_MODRM) + self.set_funcflag(0x0F9A, self.setp_rm8, CHK_MODRM) + self.set_funcflag(0x0F9B, self.setnp_rm8, CHK_MODRM) + self.set_funcflag(0x0F9C, self.setl_rm8, CHK_MODRM) + self.set_funcflag(0x0F9D, self.setnl_rm8, CHK_MODRM) + self.set_funcflag(0x0F9E, self.setle_rm8, CHK_MODRM) + self.set_funcflag(0x0F9F, self.setnle_rm8, CHK_MODRM) + + self.set_funcflag(0x80, self.code_80, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0x82, self.code_82, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0xC0, self.code_c0, CHK_MODRM | CHK_IMM8) + self.set_funcflag(0xF6, self.code_f6, CHK_MODRM) + self.set_funcflag(0xFE, self.code_fe, CHK_MODRM) + + + def code_d0_d2(self): + reg = self.instr.modrm.reg + if reg == 0: + self.rol_rm8() + elif reg == 1: + self.ror_rm8() + elif reg == 2: + self.rcl_rm8() + elif reg == 3: + self.rcr_rm8() + elif reg == 4: + self.shl_rm8() # sal + elif reg == 5: + self.shr_rm8() + elif reg == 6: + self.shl_rm8() # sal + elif reg == 5: + self.sar_rm8() + else: + raise RuntimeError(f"not implemented: 0xd0_d2 /{reg}") + + def set_funcflag(self, opcode: int, func: Callable[[Dict[str, Any]], None], flags: int): + if opcode >> 8 == 0x0F: + opcode = (opcode & 0xFF) | 0x0100 + assert opcode < 0x200 + self.instrfuncs[opcode] = func + self.chk[opcode] = flags + + def add_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + self.set_rm8(rm8 + r8) + self.emu.update_eflags_add(rm8, r8) + + def adc_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + carry = self.emu.is_carry().cast_to(Type.int_8) + self.set_rm8(rm8 + r8 + carry) + self.emu.update_eflags_adc(rm8, r8, carry) + + def add_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + self.set_r8(r8 + rm8) + self.emu.update_eflags_add(r8, rm8) + + def adc_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + carry = self.emu.is_carry().cast_to(Type.int_8) + self.set_r8(r8 + rm8 + carry) + self.emu.update_eflags_adc(r8, rm8, carry) + + def add_al_imm8(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.set_gpreg(reg8_t.AL, al + self.instr.imm8) + self.emu.update_eflags_add(al, self.instr.imm8) + + def or_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + self.set_rm8(rm8 | r8) + self.emu.update_eflags_or(rm8, r8) + + def or_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + self.set_r8(r8 | rm8) + self.emu.update_eflags_or(r8, rm8) + + def or_al_imm8(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.set_gpreg(reg8_t.AL, al | self.instr.imm8) + self.emu.update_eflags_or(al, self.instr.imm8) + + def and_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + self.set_rm8(rm8 & r8) + self.emu.update_eflags_and(rm8, r8) + + def and_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + self.set_r8(r8 & rm8) + self.emu.update_eflags_and(r8, rm8) + + def and_al_imm8(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.set_gpreg(reg8_t.AL, al & self.instr.imm8) + self.emu.update_eflags_and(al, self.instr.imm8) + + def sub_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + self.set_rm8(rm8 - r8) + self.emu.update_eflags_sub(rm8, r8) + + def sub_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + self.set_r8(r8 - rm8) + self.emu.update_eflags_sub(r8, rm8) + + def sub_al_imm8(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.set_gpreg(reg8_t.AL, al - self.instr.imm8) + self.emu.update_eflags_sub(al, self.instr.imm8) + + def xor_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + self.set_rm8(rm8 ^ r8) + self.emu.update_eflags_xor(rm8, r8) + + def xor_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + self.set_r8(r8 ^ rm8) + self.emu.update_eflags_xor(rm8, r8) + + def xor_al_imm8(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.set_gpreg(reg8_t.AL, al ^ self.instr.imm8) + self.emu.update_eflags_xor(al, self.instr.imm8) + + def cmp_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + self.emu.update_eflags_sub(rm8, r8) + + def cmp_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + self.emu.update_eflags_sub(r8, rm8) + + def cmp_al_imm8(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.update_eflags_sub(al, self.instr.imm8) + + def jo_rel8(self) -> None: + result = self.emu.is_overflow() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not result, ip, JumpKind.Boring) + + def jno_rel8(self) -> None: + result = self.emu.is_overflow() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(result, ip, JumpKind.Boring) + + def jb_rel8(self) -> None: + result = self.emu.is_carry() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not result, ip, JumpKind.Boring) + + def jnb_rel8(self) -> None: # jae + result = self.emu.is_carry() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(result, ip, JumpKind.Boring) + + def jz_rel8(self) -> None: + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not self.emu.is_zero(), ip) + + def jnz_rel8(self) -> None: + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(self.emu.is_zero(), ip) + + def jbe_rel8(self) -> None: + result = self.emu.is_carry() or self.emu.is_zero() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not result, ip, JumpKind.Boring) + + def ja_rel8(self) -> None: + result = self.emu.is_carry() or self.emu.is_zero() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(result, ip, JumpKind.Boring) + + def js_rel8(self) -> None: + result = self.emu.is_sign() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not result, ip, JumpKind.Boring) + + def jns_rel8(self) -> None: + result = self.emu.is_sign() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(result, ip, JumpKind.Boring) + + def jp_rel8(self) -> None: + result = self.emu.is_parity() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(result, ip, JumpKind.Boring) + + def jnp_rel8(self) -> None: + result = self.emu.is_parity() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not result, ip, JumpKind.Boring) + + def jl_rel8(self) -> None: + result = self.emu.is_sign() != self.emu.is_overflow() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not result, ip, JumpKind.Boring) + + def jnl_rel8(self) -> None: # jge + result = self.emu.is_sign() != self.emu.is_overflow() + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(result, ip, JumpKind.Boring) + + def jle_rel8(self) -> None: + result = self.emu.is_zero() or (self.emu.is_sign() != self.emu.is_overflow()) + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(not result, ip, JumpKind.Boring) + + def jnle_rel8(self) -> None: + result = ~self.emu.is_zero() and (self.emu.is_sign() == self.emu.is_overflow()) + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(result, ip, JumpKind.Boring) + + def test_rm8_r8(self) -> None: + rm8 = self.get_rm8() + r8 = self.get_r8() + self.emu.update_eflags_and(rm8, r8) + + def xchg_r8_rm8(self) -> None: + r8 = self.get_r8() + rm8 = self.get_rm8() + self.set_r8(rm8) + self.set_rm8(r8) + + def mov_rm8_r8(self) -> None: + r8 = self.get_r8() + self.set_rm8(r8) + + def mov_r8_rm8(self) -> None: + rm8 = self.get_rm8() + self.set_r8(rm8) + + def mov_sreg_rm16(self) -> None: + rm16 = self.get_rm16() + self.set_sreg(rm16) + + def nop(self) -> None: + pass + + def lohf(self) -> None: + flags = self.emu.get_gpreg(reg16_t.FLAGS).cast_to(Type.int_8) + self.emu.set_gpreg(reg8_t.AH, flags) + + def mov_al_moffs8(self) -> None: + self.emu.set_gpreg(reg8_t.AL, self.get_moffs8()) + + def mov_moffs8_al(self) -> None: + self.set_moffs8(self.emu.get_gpreg(reg8_t.AL)) + + def test_al_imm8(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.update_eflags_and(al, self.instr.imm8) + + def mov_r8_imm8(self) -> None: + reg = self.instr.opcode & 0b111 + self.emu.set_gpreg(reg8_t(reg), self.instr.imm8) + + def mov_rm8_imm8(self) -> None: + self.set_rm8(self.emu.lifter_instruction.constant(self.instr.imm8, Type.int_8)) + + def retf_imm16(self) -> None: + self.set_gpreg(reg16_t.SP, self.get_gpreg(reg16_t.SP) + self.instr.imm16) + ip = self.emu.pop16() + cs = self.emu.pop16() + self.emu.lifter_instruction.jump(None, self.emu.trans_v2p(0, cs, ip), jumpkind=JumpKind.Ret) + + def retf(self) -> None: + ip = self.emu.pop16() + cs = self.emu.pop16() + self.emu.set_sgreg(sgreg_t.CS, cs) + self.emu.lifter_instruction.jump(None, self.emu.trans_v2p(0, cs, ip), jumpkind=JumpKind.Ret) + + def int3(self) -> None: + self.instr.imm8 = 3 + self.int_imm8() + + def int_imm8(self) -> None: + self.emu.lifter_instruction.put(self.emu.constant(self.instr.imm8), "ip_at_syscall") + exit = self.emu.constant(self.instr.imm8 == 0x21) and self.emu.get_gpreg(reg8_t.AH) == 0x4c + self.emu.lifter_instruction.jump(~exit, self.emu.get_gpreg(reg8_t.AL), JumpKind.Exit) + self.emu.lifter_instruction.jump(None, self.emu.get_gpreg(reg16_t.AX), JumpKind.Syscall) + #raise Exception("INT %x" % self.instr.imm8) + #self.emu.queue_interrupt(self.instr.imm8, False) + + def iret(self) -> None: + ip = self.emu.pop16() + cs = self.emu.pop16() + flags = self.emu.pop16() + self.emu.set_gpreg(reg16_t.FLAGS, flags) + self.emu.set_sgreg(sgreg_t.CS, cs) + self.emu.lifter_instruction.jump(None, self.emu.trans_v2p(0, cs, ip), jumpkind=JumpKind.Ret) + + def in_al_imm8(self) -> None: + self.emu.set_gpreg(reg8_t.AL, self.emu.in_io8(self.instr.imm8)) + + def out_imm8_al(self) -> None: + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.out_io8(self.instr.imm8, al) + + def jmp(self) -> None: + ip = self.emu.get_gpreg(reg16_t.IP) + self.emu.constant(self.instr.imm8 + 2, Type.int_8).widen_signed(Type.int_16) + self.emu.lifter_instruction.jump(None, ip, JumpKind.Boring) + + def in_al_dx(self) -> None: + dx = self.emu.get_gpreg(reg16_t.DX) + self.emu.set_gpreg(reg8_t.AL, self.emu.in_io8(dx)) + + def out_dx_al(self) -> None: + dx = self.emu.get_gpreg(reg16_t.DX) + al = self.emu.get_gpreg(reg8_t.AL) + self.emu.out_io8(dx, al) + + def cli(self) -> None: + self.emu.set_interrupt(False) + + def sti(self) -> None: + self.emu.set_interrupt(True) + + def cld(self) -> None: + self.emu.set_direction(False) + + def std(self) -> None: + self.emu.set_direction(True) + + def hlt(self) -> None: + if not self.emu.chk_ring(0): + raise Exception(self.emu.EXP_GP) + self.emu.do_halt(True) + + def ltr_rm16(self) -> None: + if not self.emu.chk_ring(0): + raise Exception(self.emu.EXP_GP) + rm16 = self.get_rm16() + self.emu.set_tr(rm16) + + def mov_r32_crn(self) -> None: + crn = self.get_crn() + self.emu.set_gpreg(self.instr.modrm.rm, crn) + + def mov_crn_r32(self) -> None: + if not self.emu.chk_ring(0): + raise Exception(self.emu.EXP_GP) + r32 = self.emu.get_gpreg(self.instr.modrm.rm) + self.set_crn(r32) + + def seto_rm8(self) -> None: + self.set_rm8(self.emu.is_overflow()) + + def setno_rm8(self) -> None: + self.set_rm8(not self.emu.is_overflow()) + + def setb_rm8(self) -> None: + self.set_rm8(self.emu.is_carry()) + + def setnb_rm8(self) -> None: + self.set_rm8(not self.emu.is_carry()) + + def setz_rm8(self) -> None: + self.set_rm8(self.emu.is_zero()) + + def setnz_rm8(self) -> None: + self.set_rm8(not self.emu.is_zero()) + + def setbe_rm8(self) -> None: + self.set_rm8(self.emu.is_carry() or self.emu.is_zero()) + + def seta_rm8(self) -> None: + self.set_rm8(not (self.emu.is_carry() or self.emu.is_zero())) + + def sets_rm8(self) -> None: + self.set_rm8(self.emu.is_sign()) + + def setns_rm8(self) -> None: + self.set_rm8(not self.emu.is_sign()) + + def setp_rm8(self) -> None: + self.set_rm8(self.emu.is_parity()) + + def setnp_rm8(self) -> None: + self.set_rm8(not self.emu.is_parity()) + + def setl_rm8(self) -> None: + self.set_rm8(self.emu.is_sign() != self.emu.is_overflow()) + + def setnl_rm8(self) -> None: + self.set_rm8(self.emu.is_sign() == self.emu.is_overflow()) + + def setle_rm8(self) -> None: + self.set_rm8(self.emu.is_zero() or (self.emu.is_sign() != self.emu.is_overflow())) + + def setnle_rm8(self) -> None: + self.set_rm8( + self.instr, not self.emu.is_zero() and (self.emu.is_sign() == self.emu.is_overflow()), + ) + + def code_80(self) -> None: + reg = self.instr.modrm.reg + if reg == 0: + self.add_rm8_imm8() + elif reg == 1: + self.or_rm8_imm8() + elif reg == 2: + self.adc_rm8_imm8() + elif reg == 3: + self.sbb_rm8_imm8() + elif reg == 4: + self.and_rm8_imm8() + elif reg == 5: + self.sub_rm8_imm8() + elif reg == 6: + self.xor_rm8_imm8() + elif reg == 7: + self.cmp_rm8_imm8() + else: + raise RuntimeError(f"not implemented: 0x80 /{reg}") + + def code_82(self) -> None: + self.code_80() + + def code_c0(self) -> None: + reg = self.instr.modrm.reg + if reg == 4: + self.shl_rm8_imm8() + elif reg == 5: + self.shr_rm8_imm8() + elif reg == 6: + self.sal_rm8_imm8() + elif reg == 7: + self.sar_rm8_imm8() + else: + raise RuntimeError(f"not implemented: 0xc0 /{reg}") + + def code_f6(self) -> None: + reg = self.instr.modrm.reg + if reg == 0: + self.test_rm8_imm8() + elif reg == 2: + self.not_rm8() + elif reg == 3: + self.neg_rm8() + elif reg == 4: + self.mul_ax_al_rm8() + elif reg == 5: + self.imul_ax_al_rm8() + elif reg == 6: + self.div_al_ah_rm8() + elif reg == 7: + self.idiv_al_ah_rm8() + else: + raise RuntimeError(f"not implemented: 0xf6 /{reg}") + + def code_fe(self) -> None: + reg = self.instr.modrm.reg + if reg == 0: + self.inc_rm8() + elif reg == 1: + self.dec_rm8() + else: + raise RuntimeError(f"not implemented: 0xf6 /{reg}") + + def inc_rm8(self) -> None: + rm8 = self.get_rm8() + 1 + self.set_rm8(rm8) + self.emu.update_eflags_inc(rm8) + + def dec_rm8(self) -> None: + rm8 = self.get_rm8() - 1 + self.set_rm8(rm8) + self.emu.update_eflags_dec(rm8) + + def add_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(rm8 + self.instr.imm8) + self.emu.update_eflags_add(rm8, self.instr.imm8) + + def or_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(rm8 | self.instr.imm8) + self.emu.update_eflags_or(rm8, self.instr.imm8) + + def adc_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + cf = self.emu.is_carry() + self.set_rm8(rm8 + self.instr.imm8 + cf) + self.emu.update_eflags_add(rm8, self.instr.imm8 + cf) + + def sbb_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + cf = self.emu.is_carry() + self.set_rm8(rm8 - self.instr.imm8 - cf) + self.emu.update_eflags_sub(rm8, self.instr.imm8 + cf) + + + def and_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(rm8 & self.instr.imm8) + self.emu.update_eflags_and(rm8, self.instr.imm8) + + + def sub_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(rm8 - self.instr.imm8) + self.emu.update_eflags_sub(rm8, self.instr.imm8) + + + def xor_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(rm8 ^ self.instr.imm8) + + + def cmp_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.emu.update_eflags_sub(rm8, self.emu.constant(self.instr.imm8, Type.int_8)) + + + def shl_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(rm8 << self.instr.imm8) + self.emu.update_eflags_shl(rm8, self.emu.constant(self.instr.imm8, Type.int_8)) + + + def shr_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(rm8 >> self.instr.imm8) + self.emu.update_eflags_shr(rm8, self.instr.imm8) + + + def sal_rm8_imm8(self) -> None: + rm8_s = self.get_rm8().signed + self.set_rm8(rm8_s << self.instr.imm8) + + + def sar_rm8_imm8(self) -> None: + rm8_s = self.get_rm8() + self.set_rm8(rm8_s.sar(self.instr.imm8)) + + + def test_rm8_imm8(self) -> None: + rm8 = self.get_rm8() + imm8 = self.instr.imm8 #self.emu.get_code8(0) + #self.emu.update_eip(1) + self.emu.update_eflags_and(rm8, imm8) + + + def not_rm8(self) -> None: + rm8 = self.get_rm8() + self.set_rm8(~rm8) + + + def neg_rm8(self) -> None: + rm8_s = self.get_rm8().signed + self.set_rm8(-rm8_s) + self.emu.update_eflags_sub(0, rm8_s) + + + def mul_ax_al_rm8(self) -> None: + rm8 = self.get_rm8() + al = self.emu.get_gpreg(reg8_t.AL) + val = al * rm8 + self.emu.set_gpreg(reg16_t.AX, val) + self.emu.update_eflags_mul(al, rm8) + + + def imul_ax_al_rm8(self) -> None: + rm8_s = self.get_rm8().signed + al_s = self.emu.get_gpreg(reg8_t.AL).signed + val_s = al_s * rm8_s + self.emu.set_gpreg(reg16_t.AX, val_s) + self.emu.update_eflags_imul(al_s, rm8_s) + + + def div_al_ah_rm8(self) -> None: + rm8 = self.get_rm8().cast_to(Type.int_16) + ax = self.emu.get_gpreg(reg16_t.AX) + self.emu.set_gpreg(reg8_t.AL, ax // rm8) + self.emu.set_gpreg(reg8_t.AH, ax % rm8) + + + def idiv_al_ah_rm8(self) -> None: + rm8_s = self.get_rm8().cast_to(Type.int_16, signed=True) + ax_s = self.emu.get_gpreg(reg16_t.AX).signed + self.emu.set_gpreg(reg8_t.AL, ax_s // rm8_s) + self.emu.set_gpreg(reg8_t.AH, ax_s % rm8_s) + + def set_chsz_ad(self, ad): + self.chsz_ad = ad diff --git a/angr_platforms/X86_16/instruction.py b/angr_platforms/X86_16/instruction.py new file mode 100644 index 0000000..f0407de --- /dev/null +++ b/angr_platforms/X86_16/instruction.py @@ -0,0 +1,100 @@ +from .emulator import Emulator +from .regs import sgreg_t + +# Constants for repeat prefixes +NONE = 0 +REPZ = 1 +REPNZ = 2 + +# Constants for instruction flags +CHK_MODRM = 1 << 0 +CHK_IMM32 = 1 << 1 +CHK_IMM16 = 1 << 2 +CHK_IMM8 = 1 << 3 +CHK_PTR16 = 1 << 4 +CHK_MOFFS = 1 << 5 + +MAX_OPCODE = 0x200 + +# ModR/M byte structure +class ModRM: + def __init__(self): + self.rm = 0 # Register/memory operand + self.reg = 0 # Register operand or opcode extension + self.mod = 0 # Addressing mode + +# SIB byte structure +class SIB: + def __init__(self): + self.base = 0 # Base register + self.index = 0 # Index register + self.scale = 0 # Scaling factor + +# X86Instruction data structure +class InstrData: + def __init__(self): + self.prefix = 0 # X86Instruction prefix + self.pre_segment = 0 # Segment override prefix + self.pre_repeat = NONE # Repeat prefix + + self.segment: int = 0 # Default segment register + self.opcode = 0 # Opcode + self.modrm = ModRM() # ModR/M byte + self.sib = SIB() # SIB byte + self.disp8 = 0 # 8-bit displacement + self.disp16 = 0 # 16-bit displacement + self.disp32 = 0 # 32-bit displacement + self.imm8 = 0 # 8-bit immediate value + self.imm16 = 0 # 16-bit immediate value + self.imm32 = 0 # 32-bit immediate value + self.ptr16 = 0 # 16-bit far pointer + self.moffs = 0 # Memory offset + +# Base class for instruction handlers +class X86Instruction: + def __init__(self, emu: Emulator, instr: InstrData, mode32: bool): + self.emu = emu + self.instr = instr + self.mode32 = mode32 + self.chsz_ad = False + + def select_segment(self): + return sgreg_t(self.instr.pre_segment if self.instr.prefix else self.instr.segment) + +# Class for executing instructions + +class InstrFlags: + def __init__(self): + self.modrm = False + self.imm32 = False + self.imm16 = False + self.imm8 = False + self.ptr16 = False + self.moffs = False + self.moffs8 = False + + @property + def flags(self): + """Returns a byte representation of the flags.""" + return ( + (self.modrm << 0) + | (self.imm32 << 1) + | (self.imm16 << 2) + | (self.imm8 << 3) + | (self.ptr16 << 4) + | (self.moffs << 5) + | (self.moffs8 << 6) + ) + + @flags.setter + def flags(self, value): + """Sets the flags from a byte representation.""" + self.modrm = bool(value & 1) + self.imm32 = bool(value & (1 << 1)) + self.imm16 = bool(value & (1 << 2)) + self.imm8 = bool(value & (1 << 3)) + self.ptr16 = bool(value & (1 << 4)) + self.moffs = bool(value & (1 << 5)) + self.moffs8 = bool(value & (1 << 6)) + + diff --git a/angr_platforms/X86_16/interrupt.py b/angr_platforms/X86_16/interrupt.py new file mode 100644 index 0000000..729ec53 --- /dev/null +++ b/angr_platforms/X86_16/interrupt.py @@ -0,0 +1,50 @@ +from collections import deque + +from .access import DataAccess +from .debug import INFO +from .exception import EXCEPTION, EXP_GP +from .regs import sgreg_t + +# Constants for descriptor table registers +IDTR = 1 +TR = 3 + +class Interrupt(DataAccess): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + return + self.intr_q = deque() # Interrupt queue + self.pic_m = None # Master PIC + self.pic_s = None # Slave PIC + + def set_pic(self, pic, master): + raise NotImplementedError + + def handle_interrupt(self): + raise NotImplementedError + if not self.intr_q: + return + + n, hard = self.intr_q.popleft() + + idt_base = self.get_dtreg_base(IDTR) + idt_limit = self.get_dtreg_limit(IDTR) + idt_offset = n << 2 + + EXCEPTION(EXP_GP, idt_offset > idt_limit) + ivt = self.read_mem32(idt_base + idt_offset) + + cs = self.get_segment(sgreg_t.CS.name) + self.set_segment(sgreg_t.CS.name, ivt >> 16) + self.save_regs(False, cs) + self.set_ip(ivt & 0xFFFF) + + INFO(4, "int 0x%02x (IP : 0x%04x, sgreg_t.CS.name : 0x%04x)", n, ivt & 0xFFFF, ivt >> 16) + + def chk_irq(self): + raise NotImplementedError + + def save_regs(self, chpl, cs): + self.push16(self.get_flags()) + self.push16(cs) + self.push16(self.get_ip()) diff --git a/angr_platforms/X86_16/io.py b/angr_platforms/X86_16/io.py new file mode 100644 index 0000000..3ab3b82 --- /dev/null +++ b/angr_platforms/X86_16/io.py @@ -0,0 +1,90 @@ +from typing import Dict, Optional + +from pyvex.lifting.util.vex_helper import Type + +from .dev_io import MemoryIO, PortIO +from .memory import Memory + + +class IO: + def __init__(self, memory: Memory): + self.memory = memory + self.port_io: Dict[int, PortIO] = {} + self.port_io_map: Dict[int, int] = {} + self.mem_io: Dict[int, MemoryIO] = {} + self.mem_io_map: Dict[int, int] = {} + + def __del__(self): + self.port_io.clear() + self.mem_io.clear() + self.mem_io_map.clear() + + def set_portio(self, addr: int, length: int, dev: PortIO): + addr &= ~1 + self.port_io[addr] = dev + self.port_io_map[addr] = length + + def get_portio_base(self, addr: int) -> Optional[int]: + for i in range(5): # max_mask: 0xfff0 + base = (addr & ~1) - (2 * i) + if base in self.port_io_map: + return base if addr < base + self.port_io_map[base] else None + return None + + def in_io32(self, addr: int) -> int: + return self.lifter_instruction.dirty(Type.int_8, "x86g_dirtyhelper_IN", [addr, self.constant(32)]).cast_to(Type.int_32) + + def in_io16(self, addr: int) -> int: + return self.lifter_instruction.dirty(Type.int_8, "x86g_dirtyhelper_IN", [addr, self.constant(16)]).cast_to(Type.int_16) + + def in_io8(self, addr: int) -> int: + return self.lifter_instruction.dirty(Type.int_8, "x86g_dirtyhelper_IN", [addr, self.constant(8)]).cast_to(Type.int_8) + + def out_io32(self, addr: int, value: int): + self.lifter_instruction.dirty(Type.int_8, "x86g_dirtyhelper_OUT", [addr, value, self.constant(32)]) + + def out_io16(self, addr: int, value: int): + self.lifter_instruction.dirty(Type.int_8, "x86g_dirtyhelper_OUT", [addr, value, self.constant(16)]) + + def out_io8(self, addr: int, value: int): + self.lifter_instruction.dirty(Type.int_8, "x86g_dirtyhelper_OUT", [addr, value, self.constant(8)]) + + def set_memio(self, base: int, length: int, dev: MemoryIO): + assert base & ((1 << 12) - 1) == 0 + + dev.set_mem(self.memory, base, length) + self.mem_io[base] = dev + + for addr in range(base, base + length, 1 << 12): + self.mem_io_map[addr] = base + + def get_memio_base(self, addr: int) -> Optional[int]: + addr &= ~((1 << 12) - 1) + return self.mem_io_map.get(addr) + + def read_memio32(self, base: int, offset: int) -> int: + assert base in self.mem_io + return self.mem_io[base].read32(offset) + + def read_memio16(self, base: int, offset: int) -> int: + assert base in self.mem_io + return self.mem_io[base].read16(offset) + + def read_memio8(self, base: int, offset: int) -> int: + assert base in self.mem_io + return self.mem_io[base].read8(offset) + + def write_memio32(self, base: int, offset: int, value: int): + assert base in self.mem_io + self.mem_io[base].write32(offset, value) + + def write_memio16(self, base: int, offset: int, value: int): + assert base in self.mem_io + self.mem_io[base].write16(offset, value) + + def write_memio8(self, base: int, offset: int, value: int): + assert base in self.mem_io + self.mem_io[base].write8(offset, value) + + def chk_memio(self, addr: int) -> Optional[int]: + return self.get_memio_base(addr) diff --git a/angr_platforms/X86_16/lift_86_16.py b/angr_platforms/X86_16/lift_86_16.py new file mode 100644 index 0000000..2163a0d --- /dev/null +++ b/angr_platforms/X86_16/lift_86_16.py @@ -0,0 +1,108 @@ +#pylint: disable=wildcard-import, unused-wildcard-import, unused-argument, arguments-differ +import logging + +from pyvex.lifting import register +from pyvex.lifting.util import Instruction, ParseError, GymratLifter + +from .parse import CHSZ_AD, CHSZ_OP + +from .arch_86_16 import Arch86_16 +from .emulator import Emulator +from .instr16 import Instr16 +from .instr32 import Instr32 +from .instruction import InstrData + +logger = logging.getLogger(__name__) + + +class Instruction_ANY(Instruction): + # Convert everything that's not an instruction into a No-op to meet the BF spec + bin_format = "xxxxxxxx" # We don't care, match it all + name = "nop" + + def __init__(self, *args, **kwargs): + self.emu = Emulator() + self.instr = InstrData() + + self.instr16 = Instr16(self.emu, self.instr) + self.instr32 = Instr32(self.emu, self.instr) + self.emu.set_lifter_instruction(self) + super().__init__(*args, **kwargs) + + def parse(self, bitstrm): + self.start = bitstrm.bytepos + instr = list(self.arch.capstone.disasm(bytes(bitstrm[self.start * 8: self.start * 8 + 15 * 8]), 0, 1)) + if not instr: + raise ParseError("Couldn't disassemble instruction") + self.cs = instr[0] + logger.debug("cs dis: %s %s", self.cs.mnemonic, self.cs.op_str) + self.name = self.cs.insn_name() + + self.emu.set_bitstream(bitstrm) + + self.is_mode32 = False #emu.is_mode32() + prefix = self.instr32.parse_prefix() if self.is_mode32 else self.instr16.parse_prefix() + self.chsz_op = prefix & CHSZ_OP + chsz_ad = prefix & CHSZ_AD + + if self.is_mode32 ^ bool(self.chsz_op): + self.instr32.set_chsz_ad(not (self.is_mode32 ^ bool(chsz_ad))) + self.instr32.parse() + #assert self.name == self.instr32.instrfuncs[self.instr32.instr.opcode].__name__.split('_')[0] + else: + self.instr16.set_chsz_ad(self.is_mode32 ^ bool(chsz_ad)) + self.instr16.parse() + #assert self.name == self.instr16.instrfuncs[self.instr16.instr.opcode].__name__.split('_')[0] + self.bitwidth = (bitstrm.bytepos - self.start) * 8 + return {"x": "00000000"} + + def compute_result(self): + try: + if self.is_mode32 ^ bool(self.chsz_op): + self.instr32.exec() + else: + self.instr16.exec() + except Exception as ex: + logger.error("Exception during instruction execution: %s", ex) + raise ex from Exception + + def disassemble(self): + return self.start, self.cs.insn_name(), [str(i) for i in self.cs.operands] + +class Lifter86_16(GymratLifter): + + instrs = {Instruction_ANY} + + +register(Lifter86_16, "86_16") + + +def main(): + logging.basicConfig() + logging.getLogger().setLevel(logging.DEBUG) + tests = [ + b"\x90", # NOP + b"\xb8\x01\x02", # MOVW + b"\xc3", # RET + ] + print("Decoder test:") + for num, test in enumerate(tests): + print(num) + lifter = Lifter86_16(Arch86_16(), 0) + lifter.lift(data=test) + + print("Lifter test:") + for test in tests: + lifter = Lifter86_16(Arch86_16(), 0) + lifter.lift(data=test) + lifter.irsb.pp() + + print("Full tests:") + fulltest = b"".join(tests) + lifter = Lifter86_16(Arch86_16(), 0) + lifter.lift(data=fulltest) + lifter.irsb.pp() + + +if __name__ == "__main__": + main() diff --git a/angr_platforms/X86_16/memory.py b/angr_platforms/X86_16/memory.py new file mode 100644 index 0000000..1f5295f --- /dev/null +++ b/angr_platforms/X86_16/memory.py @@ -0,0 +1,81 @@ +from typing import Optional + +from bitstring import ConstBitStream +from pyvex.lifting.util.vex_helper import Type + +DEFAULT_MEMORY_SIZE = 1024 # 1 KB + +class Memory: + def __init__(self, size: int = DEFAULT_MEMORY_SIZE): + self.mem_size = size + self.memory = bytearray(size) + self.a20gate = False + + def __del__(self): + del self.memory + self.mem_size = 0 + + def dump_mem(self, addr: int, size: int): + addr &= ~(0x10 - 1) + + for i in range(0, size, 0x10): + print(f"0x{addr + i:08x}: ", end="") + for j in range(4): + print( + f"{int.from_bytes(self.memory[addr + i + j * 4:addr + i + (j + 1) * 4], 'little'):08x} ", + end="", + ) + print() + + def read_data(self, addr: int, size: int) -> Optional[bytearray]: + if not self.in_range(addr, size): + return None + return self.memory[addr : addr + size] + + def write_data(self, addr: int, data: bytearray) -> bool: + if not self.in_range(addr, len(data)): + return False + self.memory[addr : addr + len(data)] = data + return True + + def read_mem32(self, addr: int) -> int: + if isinstance(addr, int): + addr = self.lifter_instruction.constant(addr, Type.int_32) + return self.lifter_instruction.load(addr, Type.int_32) + + def read_mem16(self, addr: int) -> int: + if isinstance(addr, int): + addr = self.lifter_instruction.constant(addr, Type.int_32) + return self.lifter_instruction.load(addr, Type.int_16) + + def read_mem8(self, addr: int) -> int: + if isinstance(addr, int): + addr = self.lifter_instruction.constant(addr, Type.int_32) + return self.lifter_instruction.load(addr, Type.int_8) + + def write_mem32(self, addr: int, value: int): + if isinstance(addr, int): + addr = self.lifter_instruction.constant(addr, Type.int_32) + self.lifter_instruction.store(value, addr) + + def write_mem16(self, addr: int, value: int): + if isinstance(addr, int): + addr = self.lifter_instruction.constant(addr, Type.int_32) + self.lifter_instruction.store(value, addr) + + def write_mem8(self, addr: int, value: int): + if isinstance(addr, int): + addr = self.lifter_instruction.constant(addr, Type.int_32) + self.lifter_instruction.store(value, addr) + + def is_ena_a20gate(self) -> bool: + return self.a20gate + + def set_a20gate(self, ena: bool): + self.a20gate = ena + + def in_range(self, addr: int, length: int) -> bool: + return addr + length - 1 < self.mem_size + + def set_bitstream(self, bitstream): + self.bitstream: ConstBitStream = bitstream diff --git a/angr_platforms/X86_16/parse.py b/angr_platforms/X86_16/parse.py new file mode 100644 index 0000000..05f4076 --- /dev/null +++ b/angr_platforms/X86_16/parse.py @@ -0,0 +1,147 @@ +import logging +import struct +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .emulator import Emulator +from .instruction import * + +CHSZ_NONE: int = 0 +CHSZ_OP: int = 1 +CHSZ_AD: int = 2 + +logger = logging.getLogger(__name__) + +class ParseInstr(X86Instruction): + def __init__(self, emu: Emulator, instr: InstrData, mode32: bool): + super().__init__(emu, instr, mode32) + self.emu: Emulator = emu + self.chk = [InstrFlags()] * MAX_OPCODE + self.chsz_ad = False + + def parse_prefix(self) -> int: + chsz = 0 + + while True: + #code = self.emu.get_code8_(bitstream) + code = self.emu.bitstream.peek("uint:8") + match code: + case 0x26: + self.instr.pre_segment = sgreg_t.ES + case 0x2E: + self.instr.pre_segment = sgreg_t.CS + case 0x36: + self.instr.pre_segment = sgreg_t.SS + case 0x3E: + self.instr.pre_segment = sgreg_t.DS + case 0x64: + self.instr.pre_segment = sgreg_t.FS + case 0x65: + self.instr.pre_segment = sgreg_t.GS + case 0x66: + chsz |= CHSZ_OP + case 0x67: + chsz |= CHSZ_AD + case 0xF2: + self.instr.pre_repeat = REPNZ + case 0xF3: + self.instr.pre_repeat = REPZ + case _: + return chsz + + self.emu.bitstream.read("uint:8") + self.instr.prefix = code + #self.emu.update_eip(1) + + def parse(self) -> None: + self.parse_opcode() + + opcode = self.instr.opcode + if opcode >> 8 == 0x0F: + opcode = (opcode & 0xFF) | 0x0100 + + if opcode not in self.chk: + + raise RuntimeError(f"Unknown opcode {self.emu.bitstream.bytepos:08x}: {opcode:02x}{self.emu.bitstream.peek('uint:32'):08x}") + #sys.exit(1) + if self.chk[opcode] & CHK_MODRM: + self.parse_modrm_sib_disp() + + if self.chk[opcode] & CHK_IMM32: + self.instr.imm32 = self.emu.get_code32(0) + #self.emu.update_eip(4) + elif self.chk[opcode] & CHK_IMM16: + self.instr.imm16 = self.emu.get_code16(0) + #self.emu.update_eip(2) + elif self.chk[opcode] & CHK_IMM8: + self.instr.imm8 = struct.unpack("b", struct.pack("B", self.emu.get_code8(0)))[0] + #self.emu.update_eip(1) + if self.chk[opcode] & CHK_PTR16: + self.instr.ptr16 = self.emu.get_code16(0) + #self.emu.update_eip(2) + + if self.chk[opcode] & CHK_MOFFS: + self.parse_moffs() + + if opcode == 0xf6 and self.instr.modrm.reg == 0: #test + self.instr.modrm.imm8 = self.emu.get_code8(0) + if opcode == 0xf7 and self.instr.modrm.reg == 0: #test + self.instr.modrm.imm16 = self.emu.get_code16(0) + + + def parse_opcode(self) -> None: + self.instr.opcode = self.emu.get_code8(0) + #self.emu.update_eip(1) + + # two byte opcode + if self.instr.opcode == 0x0F: + self.instr.opcode = (self.instr.opcode << 8) + self.emu.get_code8(0) + #self.emu.update_eip(1) + logger.warning(f"opcode: {self.instr.opcode:0x}") + + def parse_modrm_sib_disp(self) -> None: + modrm = self.emu.get_code8(0) + self.instr.modrm.mod = modrm >> 6 + self.instr.modrm.reg = (modrm >> 3) & 0b111 + self.instr.modrm.rm = modrm & 0b111 + #self.emu.update_eip(1) + + if self.emu.is_mode32() ^ self.chsz_ad: + self.parse_modrm32() + else: + self.parse_modrm16() + + def parse_modrm32(self) -> None: + if self.instr.modrm.mod != 3 and self.instr.modrm.rm == 4: + sib = self.emu.get_code8(0) + self.instr.sib.scale = sib >> 6 + self.instr.sib.index = (sib >> 3) & 0b111 + self.instr.sib.base = sib & 0b111 + #self.emu.update_eip(1) + + if ( + self.instr.modrm.mod == 2 + or (self.instr.modrm.mod == 0 and self.instr.modrm.rm == 5) + or (self.instr.modrm.mod == 0 and self.instr.sib.base == 5) + ): + self.instr.disp32 = self.emu.get_code32(0) + #self.emu.update_eip(4) + elif self.instr.modrm.mod == 1: + self.instr.disp8 = struct.unpack("b", struct.pack("B", self.emu.get_code8(0)))[0] + #self.emu.update_eip(1) + + def parse_modrm16(self) -> None: + if (self.instr.modrm.mod == 0 and self.instr.modrm.rm == 6) or self.instr.modrm.mod == 2: + self.instr.disp16 = self.emu.get_code16(0) + #self.emu.update_eip(2) + elif self.instr.modrm.mod == 1: + self.instr.disp8 = struct.unpack("b", struct.pack("B", self.emu.get_code8(0)))[0] + #self.emu.update_eip(1) + + def parse_moffs(self) -> None: + if self.emu.is_mode32() ^ self.chsz_ad: + self.instr.moffs = self.emu.get_code32(0) + #self.emu.update_eip(4) + else: + self.instr.moffs = self.emu.get_code16(0) + #self.emu.update_eip(2) diff --git a/angr_platforms/X86_16/processor.py b/angr_platforms/X86_16/processor.py new file mode 100644 index 0000000..477472d --- /dev/null +++ b/angr_platforms/X86_16/processor.py @@ -0,0 +1,291 @@ +from pyvex.lifting.util.vex_helper import Type + +from .cr import CR +from .eflags import Eflags +from .regs import dtreg_t, reg8_t, reg16_t, reg32_t, sgreg_t + +# Constants for general-purpose registers + +# Constants for segment registers + +# Constants for descriptor table registers + + +TYPES = {reg8_t: Type.int_8, reg16_t: Type.int_16, reg32_t: Type.int_32, sgreg_t: Type.int_16} + +# General-purpose register structure + +class GPRegister: + def __init__(self): + self.reg32 = 0 # 32-bit register value + + @property + def reg16(self): + return self.reg32 & 0xFFFF + + @reg16.setter + def reg16(self, value): + self.reg32 = (self.reg32 & 0xFFFF0000) | (value & 0xFFFF) + + @property + def reg8_l(self): + return self.reg32 & 0xFF + + @reg8_l.setter + def reg8_l(self, value): + self.reg32 = (self.reg32 & 0xFFFFFF00) | (value & 0xFF) + + @property + def reg8_h(self): + return (self.reg32 >> 8) & 0xFF + + @reg8_h.setter + def reg8_h(self, value): + self.reg32 = (self.reg32 & 0xFFFF00FF) | ((value & 0xFF) << 8) + +# Segment register cache structure + +class SGRegCache: + def __init__(self): + self.base = 0 # Base address of the segment + self.limit = 0 # Limit of the segment + self.flags = SegDescFlags() # Flags for the segment descriptor + +# Segment descriptor flags structure + +class SegDescFlags: + def __init__(self): + self.raw = 0 # Raw flags value + + @property + def type(self): + return self.raw & 0xF + + @type.setter + def type(self, value): + self.raw = (self.raw & 0xFFF0) | (value & 0xF) + + @property + def S(self): + return bool(self.raw & (1 << 4)) + + @S.setter + def S(self, value): + self.raw = (self.raw & ~(1 << 4)) | (value << 4) + + @property + def DPL(self): + return (self.raw >> 5) & 3 + + @DPL.setter + def DPL(self, value): + self.raw = (self.raw & ~(3 << 5)) | (value << 5) + + @property + def P(self): + return bool(self.raw & (1 << 7)) + + @P.setter + def P(self, value): + self.raw = (self.raw & ~(1 << 7)) | (value << 7) + + @property + def AVL(self): + return bool(self.raw & (1 << 8)) + + @AVL.setter + def AVL(self, value): + self.raw = (self.raw & ~(1 << 8)) | (value << 8) + + @property + def DB(self): + return bool(self.raw & (1 << 10)) + + @DB.setter + def DB(self, value): + self.raw = (self.raw & ~(1 << 10)) | (value << 10) + + @property + def G(self): + return bool(self.raw & (1 << 11)) + + @G.setter + def G(self, value): + self.raw = (self.raw & ~(1 << 11)) | (value << 11) + +# Segment register structure +class SGRegister: + def __init__(self): + self.raw = 0 # Raw segment selector value + self.cache = SGRegCache() # Cached segment descriptor information + + @property + def RPL(self): + return self.raw & 3 + + @RPL.setter + def RPL(self, value): + self.raw = (self.raw & ~3) | (value & 3) + + @property + def TI(self): + return bool(self.raw & (1 << 2)) + + @TI.setter + def TI(self, value): + self.raw = (self.raw & ~(1 << 2)) | (value << 2) + + @property + def index(self): + return (self.raw >> 3) & 0x1FFF + + @index.setter + def index(self, value): + self.raw = (self.raw & 0x7) | ((value & 0x1FFF) << 3) + +# Descriptor table register structure +class DTRegister: + def __init__(self): + self.selector = 0 # Selector for LDTR and TR + self.base = 0 # Base address of the descriptor table + self.limit = 0 # Limit of the descriptor table + +# Processor class +class Processor(Eflags, CR): + def __init__(self): + super().__init__() + return + + self.eip = 0 # X86Instruction pointer + self.gpregs = [GPRegister() for _ in range(reg32_t.GPREGS_COUNT.value)] # General-purpose registers + self.sgregs = [SGRegister() for _ in range(sgreg_t.SGREGS_COUNT.value)] # Segment registers + self.dtregs = [DTRegister() for _ in range(dtreg_t.DTREGS_COUNT.value)] # Descriptor table registers + + self.halt = False + + self.set_eip(0xFFFF0) + self.set_crn(0, 0x60000010) + self.set_eflags(2) + + self.sgregs[sgreg_t.CS.value].raw = 0xF000 + self.sgregs[sgreg_t.CS.value].cache.base = 0xFFFF0000 + self.sgregs[sgreg_t.CS.value].cache.flags.type = 0x18 # Code segment + for i in range(sgreg_t.SGREGS_COUNT.value): + self.sgregs[i].cache.limit = 0xFFFF + self.sgregs[i].cache.flags.P = 1 + self.sgregs[i].cache.flags.S = 1 + self.sgregs[i].cache.flags.type = 0x10 # Data segment + + self.dtregs[dtreg_t.IDTR.value].base = 0 + self.dtregs[dtreg_t.IDTR.value].limit = 0xFFFF + self.dtregs[dtreg_t.GDTR.value].base = 0 + self.dtregs[dtreg_t.GDTR.value].limit = 0xFFFF + self.dtregs[dtreg_t.LDTR.value].base = 0 + self.dtregs[dtreg_t.LDTR.value].limit = 0xFFFF + + def dump_regs(self): + gpreg_name = ["EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI"] + sgreg_name = ["ES", "CS", "SS", "DS", "FS", "GS"] + dtreg_name = ["GDTR", "IDTR", "LDTR", " TR "] + + print(f"EIP = 0x{self.eip:08x}") + for i in range(reg32_t.GPREGS_COUNT.value): + print( + f"{gpreg_name[i]} = 0x{self.gpregs[i].reg32:08x} : 0x{self.gpregs[i].reg16:04x} (0x{self.gpregs[i].reg8_h:02x}/0x{self.gpregs[i].reg8_l:02x})", + ) + print(f"EFLAGS = 0x{self.get_eflags():08x}") + + for i in range(sgreg_t.SGREGS_COUNT.value): + cache = self.sgregs[i].cache + print( + f"{sgreg_name[i]} = 0x{self.sgregs[i].raw:04x} {{base = 0x{cache.base:08x}, limit = {cache.limit:08x}, flags = {cache.flags.raw:04x}}}", + ) + + for i in range(dtreg_t.LDTR.value): + print( + f"{dtreg_name[i]} = {{base = 0x{self.dtregs[i].base:08x}, limit = {self.dtregs[i].limit:08x}}}", + ) + for i in range(dtreg_t.LDTR.value, dtreg_t.DTREGS_COUNT.value): + print( + f"{dtreg_name[i]} = 0x{self.dtregs[i].selector:04x} {{base = 0x{self.dtregs[i].base:08x}, limit = {self.dtregs[i].limit:08x}}}", + ) + + for i in range(5): + print(f"CR{i}=0x{self.get_crn(i):08x} ", end="") + print() + + def get_eip(self): + return self.eip + + def get_ip(self): + return self.lifter_instruction.get("ip", Type.int_16) + return self.eip & 0xFFFF + + def get_gpreg(self, n): + return self.lifter_instruction.get(n.name.lower(), TYPES[type(n)]) + + def constant(self, n, type_=Type.int_8): + return self.lifter_instruction.constant(n, type_) + + def get_sgreg(self, n): + return self.lifter_instruction.get(n.name.lower(), TYPES[type(n)]) + + def get_dtreg_selector(self, n): + #assert n < dtreg_t.DTREGS_COUNT.value + return self.dtregs[n].selector + + def get_dtreg_base(self, n): + assert n < dtreg_t.DTREGS_COUNT.value + return self.dtregs[n].base + + def get_dtreg_limit(self, n): + assert n < dtreg_t.DTREGS_COUNT.value + return self.dtregs[n].limit + + def set_eip(self, value): + self.eip = value + + def set_ip(self, value): + assert False + self.set_gpreg(self, "ip", self.lifter_instruction.constant(value, Type.int_16)) + + def set_gpreg(self, n, value): + if isinstance(value, int): + val = self.lifter_instruction.constant(value, TYPES[type(n)]) + else: + val = value + self.lifter_instruction.put(val, n.name.lower()) + + def set_sgreg(self, n, reg): + self.set_gpreg(n, reg) + + def set_dtreg(self, n, sel, base, limit): + assert n < dtreg_t.DTREGS_COUNT.value + self.dtregs[n].selector = sel + self.dtregs[n].base = base + self.dtregs[n].limit = limit + + def update_eip(self, value): + return self.update_gpreg(reg32_t.EIP, value) + + def update_ip(self, value): + return self.update_gpreg(reg16_t.IP, value) + + def update_gpreg(self, n, value): + result = self.get_gpreg(n) + result += value + self.set_gpreg(n, result) + return result + + def is_halt(self): + return self.halt + + def do_halt(self, h): + self.halt = h + + def is_mode32(self): + return False + return self.sgregs[sgreg_t.CS.value].cache.flags.DB + + def set_lifter_instruction(self, lifter_instruction): + self.lifter_instruction = lifter_instruction diff --git a/angr_platforms/X86_16/regs.py b/angr_platforms/X86_16/regs.py new file mode 100644 index 0000000..69375cb --- /dev/null +++ b/angr_platforms/X86_16/regs.py @@ -0,0 +1,57 @@ +from enum import Enum + + +class reg32_t(Enum): + EAX = 0 + ECX = 1 + EDX = 2 + EBX = 3 + ESP = 4 + EBP = 5 + ESI = 6 + EDI = 7 + GPREGS_COUNT = 8 + EIP = 9 + EFLAGS = 10 + + +class reg16_t(Enum): + AX = 0 + CX = 1 + DX = 2 + BX = 3 + SP = 4 + BP = 5 + SI = 6 + DI = 7 + IP = 8 + FLAGS = 9 + + +class reg8_t(Enum): + AL = 0 + CL = 1 + DL = 2 + BL = 3 + AH = 4 + CH = 5 + DH = 6 + BH = 7 + + +class sgreg_t(Enum): + ES = 0 + CS = 1 + SS = 2 + DS = 3 + FS = 4 + GS = 5 + SGREGS_COUNT = 6 + + +class dtreg_t(Enum): + GDTR = 0 + IDTR = 1 + LDTR = 2 + TR = 3 + DTREGS_COUNT = 4 diff --git a/angr_platforms/X86_16/simos_86_16.py b/angr_platforms/X86_16/simos_86_16.py new file mode 100644 index 0000000..bfc015f --- /dev/null +++ b/angr_platforms/X86_16/simos_86_16.py @@ -0,0 +1,42 @@ +from angr.calling_conventions import ( + SimCC, + SimRegArg, + SimStackArg, + register_default_cc, + register_syscall_cc, +) + +from .arch_86_16 import Arch86_16 + + +class SimDOSintcall(SimCC): + ARG_REGS = ["ax", "bx", "cx", "dx"] # TODO + RETURN_VAL = SimRegArg("ax", 2) + ARCH = Arch86_16 + + @staticmethod + def _match(arch, args: list, sp_delta): # pylint: disable=unused-argument + # doesn't appear anywhere but syscalls + return False + + @staticmethod + def syscall_num(state): + print("DOS int was called %s" % state.regs.ip_at_syscall) + return state.regs.ax + + +class SimCC8616MSC(SimCC): + ARG_REGS = [] + FP_ARG_REGS = [] + STACKARG_SP_DIFF = 2 + RETURN_ADDR = SimStackArg(0, 2) + RETURN_VAL = SimRegArg("ax", 2) + OVERFLOW_RETURN_VAL = SimRegArg("dx", 2) + ARCH = Arch86_16 + STACK_ALIGNMENT = 2 + CALLEE_CLEANUP = True + + +register_default_cc("86_16", SimCC8616MSC) +register_syscall_cc("86_16", "Linux", SimDOSintcall) +register_syscall_cc("86_16", "default", SimDOSintcall) diff --git a/tests/test_x86_16bit.py b/tests/test_x86_16bit.py new file mode 100644 index 0000000..08354b1 --- /dev/null +++ b/tests/test_x86_16bit.py @@ -0,0 +1,298 @@ +import logging +import re + +import angr +import claripy + +import keystone as ks + +from archinfo import ArchX86 + +from angr_platforms.X86_16.arch_86_16 import Arch86_16 +from angr_platforms.X86_16.lift_86_16 import Lifter86_16 # noqa +from angr_platforms.X86_16.simos_86_16 import SimCC8616MSC # noqa + +logging.getLogger("angr.storage.memory_mixins.default_filler_mixin").setLevel("ERROR") +logging.getLogger("pyvex.expr").setLevel("DEBUG") +logging.getLogger("angr_platforms.X86_16.parse").setLevel("DEBUG") + +FLAGS = {"CF": 0, "PF": 2, "AF": 4, "ZF": 6, "SF": 7, "DF": 10, "OF": 11} + + +def assembler(lines, bitness=0) -> bytes: + ks_ = ks.Ks(ks.KS_ARCH_X86, {16: ks.KS_MODE_16, 32: ks.KS_MODE_32}[bitness]) + data, count = ks_.asm(lines, as_bytes=True) + print(data) + return data + + +def step(simgr, insn_bytes): + # Step to the next instruction (execute the current block) + simgr.step(num_inst=1, insn_bytes=insn_bytes) + # Get the new state after execution + new_state = simgr.active + return new_state + + +def prepare(arch, data): + # Create an Angr project + addr = 0x100 # 0x400000 + project = angr.load_shellcode(data, arch=arch, start_offset=addr, load_address=addr, selfmodifying_code=False, + rebase_granularity=0x1000) + # Lift the instruction to VEX + state = project.factory.blank_state() + # Execute the instruction + block = project.factory.block(state.addr, len(data)) + block.vex.pp() # Print the VEX IR for inspection + block.pp() # Print the block for inspection + # Create a simulation manager + simgr = project.factory.simgr(state) + return simgr + + +def compare_states(instruction, state32_, state16_): + differencies = [] + for state32, state16 in zip(state32_, state16_): + state16.regs.eip &= 0xffff + skip_regs = {"eflags", "d"} + if not instruction.startswith("j") and not instruction.startswith("l"): + skip_regs.add("eip") + # Compare registers + for reg in state16.arch.register_list: + reg_name = reg.name + if reg_name in skip_regs: + continue + val32 = repr(claripy.simplify(getattr(state32.regs, reg_name))) + try: + val16 = repr(claripy.simplify(getattr(state16.regs, reg_name))) + + if val32 != val16: + val32 = filter_symbolic(val32) + val16 = filter_symbolic(val16) + print(f"Register {reg_name} differs: state32={val32}\n state16={val16}") + differencies.append((reg_name, val32, val16)) + except KeyError: + pass + # print(f"Register {reg_name} not found in state") + + # To handle lazy flag calculation, print individual flags + flags32 = {key: state32.regs.flags[bit] for key, bit in FLAGS.items()} + flags16 = {key: state16.regs.flags[bit] for key, bit in FLAGS.items()} + for flag, value32 in flags32.items(): + if flag not in {"CF", "ZF", "SF", "OF"}: + continue + value32 = repr(claripy.simplify(flags32[flag])) + value16 = repr(claripy.simplify(flags16[flag])) + + if value32 != value16: + value32 = filter_symbolic(value32) + value16 = filter_symbolic(value16) + print(f"Flag {flag} differs: state32={value32}\n state16={value16}") + differencies.append((flag, value32, value16)) + return differencies + + +def filter_symbolic(value32): + value32 = value32.replace("{UNINITIALIZED}", "").replace("reg_", "") + value32 = re.sub(r"_\d_32", "", value32) + value32 = re.sub(r"\[(\d+):\1]", r"[\g<1>]", value32) + return value32 + + +def compare_instructions_impact(instruction: str): + arch_16 = Arch86_16() # get architecture + arch_32 = ArchX86() # get architecture + print("~~32~~") + bytes32 = assembler(instruction, 32) + simgr32 = prepare(arch_32, bytes32) + print("~~16~~") + bytes16 = assembler(instruction, 16) + + print(bytes16) + simgr16 = prepare(arch_16, bytes16) + current_state32 = simgr32.active[0] + current_state16 = simgr16.active[0] + current_state16.regs.d = current_state16.regs.eflags[10] + for reg in current_state16.arch.register_list: + val16 = getattr(current_state16.regs, reg.name) + try: + setattr(current_state32.regs, reg.name, val16) + except Exception as ex: + print(f"Register {reg.name} failed to set %s", ex) + print("~~will step 32~~") + stage32 = step(simgr32, bytes32) + print("~~will step 16~") + stage16 = step(simgr16, bytes16) + print("~~compare impact~~") + return compare_states(instruction, stage32, stage16) + + +TODO = """ +imul si,si,0x3 ; TODO cf, of +imul si,si,0x1234 ; TODO cf, of +jmp 0x1ea # working. assember issue +jmp -35 # working +jmp 0xffffff35 # working +shl dx,1 # flags correct +shl dx,0 # TODO flags +shl ax,1 # cf, of +imul ax,ax,0x6 # TODO cf, of +jno 0x106 # assembler +jo 0x106 +jns 0x106 +js 0x106 +jnc 0x106 +jc 0x106 +jna 0x106 +ja 0x106 +jnl 0x106 +jl 0x106 +jng 0x106 +jg 0x106 # assembler +shl bx,cl # disagree +shl si,cl # disagree +shr di,cl # disagree + +call 0x17a # assembler +call 0xfd92 # assembler +callf [0x2e0:0xb38] # assembler + +pop bp # ?? +pop di +push ax +push cs +push si + +rol si,cl # TODO +ror si,cl # TODO +rcl si,cl # TODO +rcr si,cl # TODO +mul bx +div cx +div cl +mul bl +movsw # TODO +idiv cx # TODO +in al,dx +""" + +LIST = """ +and ax,cx +or ax,cx +xor ax,cx +cmp ax,cx +sub ax,cx +xchg ax,cx +add ax,cx + +and al,cl +or al,cl +xor al,cl +cmp al,cl +sub al,cl +xchg al,cl +add al,cl + +std +sti + +int 0x21 +add bx,0x10 +add bx,dx +add cx,2 +add sp,2 +and al,3 +and ax,0xf +and bx,0xfff0 +cdq +cld +cli +cmp bp,di +cmp al,1 +cmp ax,0x15 +cmp ax,8 +cmp cx,ax +cmp di,0x200 +dec cx +mov bx,0x1234 +inc bx +je 0x25 +jnz 6 +jcxz 0x7b +jge 0x2e +jl 0x11 +jle 5 +jmp 5 +mov ah,0x0 +mov ax,0x1a +mov ax,0x2500 +mov ax,di +mov bp,sp +mov bx,0x0 +mov bx,ax +mov bx,si +mov ch,al +mov cl,0x4 +mov cl,4 +mov cx,0x7fff +mov cx,0x96 +mov cx,ax +mov di,0x200 +mov di,ax +mov ds,dx +mov dx,0x171 +mov dx,bx +mov dx,cs +mov dx,di +mov dx,ss +mov es,ax +mov es,di +mov si,0x452e +mov si,ax +mov sp,bp +mov sp,di +mov ss,dx +jno 6 +jo 6 +jns 6 +js 6 +jnc 6 +jc 6 +jna 6 +ja 6 +jnl 6 +jl 6 +jng 6 +jg 6 +neg cx +nop +or al,al +or ax,ax +or ch,0x80 +not cx +ret +retf +sti +sub ah,ah +sub al,0x4a +sub ax,ax +sub bp,dx +sub cl,cl +sub sp,0x34 +xchg bx,ax +xor ah,ah +xor bp,bp + +out dx,al +""" + + +def test_instructions(): + for line in filter(None, LIST.splitlines()): + result = compare_instructions_impact(line) + assert not result, result + print("Success!") + + +if __name__ == '__main__': + test_instructions()