mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
simple_x86/64 now handle prefix and prefixed mem_access
This commit is contained in:
+148
-120
@@ -74,36 +74,113 @@ class BitArray(object):
|
||||
def copy(self):
|
||||
return type(self)(self.size, self.array)
|
||||
|
||||
# Rules: bytes only !!!!
|
||||
# Prefix
|
||||
class Prefix(object):
|
||||
PREFIX_VALUE = None
|
||||
def __init__(self, next=None):
|
||||
self.next = next
|
||||
|
||||
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp'])
|
||||
def __add__(self, other):
|
||||
return type(self)(other)
|
||||
|
||||
def get_code(self):
|
||||
return chr(self.PREFIX_VALUE) + self.next.get_code()
|
||||
|
||||
def create_prefix(name, value):
|
||||
prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE' : value})
|
||||
setattr(sys.modules[__name__], name, prefix_type())
|
||||
|
||||
create_prefix('LockPrefix', 0xf0)
|
||||
create_prefix('Repne', 0xf2)
|
||||
create_prefix('Rep', 0xf3)
|
||||
create_prefix('SSPrefix', 0x36)
|
||||
create_prefix('CSPrefix', 0x2e)
|
||||
create_prefix('DSPrefix', 0x3e)
|
||||
create_prefix('ESPrefix', 0x26)
|
||||
create_prefix('FSPrefix', 0x64)
|
||||
create_prefix('GSPrefix', 0x65)
|
||||
create_prefix('OperandSizeOverride', 0x66)
|
||||
create_prefix('AddressSizeOverride', 0x67)
|
||||
|
||||
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp', 'prefix'])
|
||||
|
||||
reg_order = ['RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI']
|
||||
new_reg_order = ['R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15']
|
||||
x64_regs = reg_order + new_reg_order
|
||||
|
||||
x64_segment_selectors = {'CS' : CSPrefix, 'DS' : DSPrefix, 'ES' : ESPrefix, 'SS' : SSPrefix,
|
||||
'FS': FSPrefix, 'GS' : GSPrefix}
|
||||
|
||||
class X64(object):
|
||||
@staticmethod
|
||||
def is_reg(name):
|
||||
try:
|
||||
return (name.upper() in reg_order) or X64.is_new_reg(name)
|
||||
except AttributeError: # Not a string
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_new_reg(name):
|
||||
try:
|
||||
return name.upper() in new_reg_order
|
||||
except AttributeError: # Not a string
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_mem_acces(data):
|
||||
return isinstance(data, mem_access)
|
||||
|
||||
@staticmethod
|
||||
def mem_access_has_only(mem_access, names):
|
||||
if not X64.is_mem_acces(mem_access):
|
||||
raise ValueError("mem_access_has_only")
|
||||
for f in mem_access._fields:
|
||||
if f != "prefix" and getattr(mem_access, f) and f not in names:
|
||||
return False
|
||||
if "base" in names and mem_access.base is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def to_little_endian(i, size=64):
|
||||
pack = {8: 'B', 16 : 'H', 32 : 'I', 64 : 'Q'}
|
||||
s = pack[size]
|
||||
mask = (1 << size) - 1
|
||||
i = i & mask
|
||||
return struct.unpack("<" + s, struct.pack(">" + s, i))[0]
|
||||
|
||||
|
||||
def create_displacement(base=None, index=None, scale=None, disp=0):
|
||||
def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None):
|
||||
if index is not None and scale is None:
|
||||
scale = 1
|
||||
if scale and index is None:
|
||||
raise ValueError("Cannot create displacement with scale and no index")
|
||||
if scale and index.upper() == "RSP":
|
||||
raise ValueError("Cannot create displacement with index == RSP")
|
||||
return mem_access(base, index, scale, disp)
|
||||
return mem_access(base, index, scale, disp, prefix)
|
||||
|
||||
def mem(data):
|
||||
"""Parse a memory access string"""
|
||||
"""Parse a memory access string of format [EXPR] or seg:[EXPR]
|
||||
EXPR may describe: BASE | INDEX * SCALE | DISPLACEMENT or any combinaison (in this order)
|
||||
"""
|
||||
if not isinstance(data, str):
|
||||
raise TypeError("mem need a string to parse")
|
||||
data = data.strip()
|
||||
prefix = None
|
||||
if not (data.startswith("[") and data.endswith("]")):
|
||||
raise ValueError("mem acces expect <[EXPR]>")
|
||||
if data[2] != ":":
|
||||
raise ValueError("mem acces expect <[EXPR]> or <seg:[EXPR]")
|
||||
prefix_name = data[:2].upper()
|
||||
if prefix_name not in x64_segment_selectors:
|
||||
raise ValueError("Unknow segment selector {0}".format(prefix_name))
|
||||
prefix = prefix_name
|
||||
data = data[3:]
|
||||
if not (data.startswith("[") and data.endswith("]")):
|
||||
raise ValueError("mem acces expect <[EXPR]> or <seg:[EXPR]")
|
||||
# A l'arrache.. j'aime pas le parsing de trucs
|
||||
data = data[1:-1]
|
||||
items = data.split("+")
|
||||
parsed_items = {}
|
||||
parsed_items = {'prefix' : prefix}
|
||||
for item in items:
|
||||
item = item.strip()
|
||||
# Index * scale
|
||||
@@ -144,12 +221,13 @@ def mem(data):
|
||||
return create_displacement(**parsed_items)
|
||||
|
||||
|
||||
|
||||
class X64RegisterSelector(object):
|
||||
|
||||
reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)}
|
||||
new_reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(new_reg_order)}
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
x = args[0]
|
||||
try:
|
||||
return (1, self.reg_opcode[x.upper()], None)
|
||||
@@ -167,8 +245,20 @@ class X64RegisterSelector(object):
|
||||
except KeyError:
|
||||
return cls.new_reg_opcode[name.upper()]
|
||||
|
||||
class FixedRegister(object):
|
||||
def __init__(self, register):
|
||||
self.reg = register.upper()
|
||||
|
||||
def accept_arg(self, args, instr_state):
|
||||
x = args[0]
|
||||
if isinstance(x, str) and x.upper() == self.reg:
|
||||
return 1, BitArray(0, []), None
|
||||
return None, None, None
|
||||
|
||||
RegisterRax = lambda: FixedRegister('RAX')
|
||||
|
||||
class RawBits(BitArray):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
return (0, self.copy(), None)
|
||||
|
||||
class ImmediatOverflow(ValueError):
|
||||
@@ -203,7 +293,7 @@ def accept_as_64immediat(x):
|
||||
raise ImmediatOverflow("64bits signed Immediat overflow")
|
||||
|
||||
class Imm8(object):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
x = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
@@ -215,7 +305,7 @@ class Imm8(object):
|
||||
return (1, BitArray.from_string(imm8), None)
|
||||
|
||||
class Imm16(object):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
x = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
@@ -227,7 +317,7 @@ class Imm16(object):
|
||||
return (1, BitArray.from_string(imm16), None)
|
||||
|
||||
class Imm32(object):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
x = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
@@ -239,7 +329,7 @@ class Imm32(object):
|
||||
return (1, BitArray.from_string(imm32), None)
|
||||
|
||||
class Imm64(object):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
x = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
@@ -251,42 +341,28 @@ class Imm64(object):
|
||||
return (1, BitArray.from_string(imm64), None)
|
||||
|
||||
class Mov_RAX_OFF64(object):
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
if RegisterRax().accept_arg(previous, args) == (None, None, None):
|
||||
def accept_arg(self, args, instr_state):
|
||||
if RegisterRax().accept_arg(args, instr_state) == (None, None, None):
|
||||
return (None, None, None)
|
||||
arg2 = args[1]
|
||||
if not (X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"])):
|
||||
return (None, None, None)
|
||||
# Migth Raise an ImmediatOverflow bu no other encoding for this so precise error is cool
|
||||
if arg2.prefix is not None:
|
||||
instr_state.prefixes.append(x64_segment_selectors[arg2.prefix])
|
||||
return (2, BitArray.from_int(8, 0xa1) + BitArray.from_string(accept_as_64immediat(arg2.disp)) , BitArray.from_int(8, 0x48))
|
||||
|
||||
class Mov_OFF64_RAX(object):
|
||||
def accept_arg(self, previous, args):
|
||||
if RegisterRax().accept_arg(previous, args[1:]) == (None, None, None):
|
||||
def accept_arg(self, args, instr_state):
|
||||
if RegisterRax().accept_arg(args[1:], instr_state) == (None, None, None):
|
||||
return (None, None, None)
|
||||
arg2 = args[0]
|
||||
if not (X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"])):
|
||||
return (None, None, None)
|
||||
if arg2.prefix is not None:
|
||||
instr_state.prefixes.append(x64_segment_selectors[arg2.prefix])
|
||||
return (2, BitArray.from_int(8, 0xa3) + BitArray.from_string(accept_as_64immediat(arg2.disp)) , BitArray.from_int(8, 0x48))
|
||||
|
||||
class RegisterRax(object):
|
||||
def accept_arg(self, previous, args):
|
||||
x = args[0]
|
||||
if isinstance(x, str) and x.upper() == 'RAX':
|
||||
return (1, BitArray(0, []), None)
|
||||
return None, None, None
|
||||
|
||||
class FixedRegister(object):
|
||||
def __init__(self, register):
|
||||
self.reg = register.upper()
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
x = args[0]
|
||||
if isinstance(x, str) and x.upper() == self.reg:
|
||||
return 1, BitArray(0, []), None
|
||||
return None, None, None
|
||||
|
||||
class ModRM(object):
|
||||
size = 8
|
||||
|
||||
@@ -295,78 +371,28 @@ class ModRM(object):
|
||||
self.accept_reverse = accept_reverse
|
||||
self.has_direction_bit = has_direction_bit
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
if len(args) < 2:
|
||||
raise ValueError("Missing arg for modrm")
|
||||
arg1 = args[0]
|
||||
arg2 = args[1]
|
||||
for sub in self.sub:
|
||||
if sub.match(arg1, arg2):
|
||||
d = sub(arg1, arg2, 0)
|
||||
d = sub(arg1, arg2, 0, instr_state)
|
||||
if self.has_direction_bit:
|
||||
previous[0][-2] = d.direction
|
||||
instr_state.previous[0][-2] = d.direction
|
||||
rex = d.rex if d.is_rex_needed else None
|
||||
return (2, d.mod + d.reg + d.rm + d.after, rex)
|
||||
elif self.accept_reverse and sub.match(arg2, arg1):
|
||||
d = sub(arg2, arg1, 1)
|
||||
d = sub(arg2, arg1, 1, instr_state)
|
||||
if self.has_direction_bit:
|
||||
previous[0][-2] = d.direction
|
||||
instr_state.previous[0][-2] = d.direction
|
||||
rex = d.rex if d.is_rex_needed else None
|
||||
return (2, d.mod + d.reg + d.rm + d.after, rex)
|
||||
return (None, None, None)
|
||||
|
||||
|
||||
class X64(object):
|
||||
|
||||
@staticmethod
|
||||
def is_reg(name):
|
||||
try:
|
||||
return (name.upper() in reg_order) or X64.is_new_reg(name)
|
||||
except AttributeError: # Not a string
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_new_reg(name):
|
||||
try:
|
||||
return name.upper() in new_reg_order
|
||||
except AttributeError: # Not a string
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_mem_acces(data):
|
||||
return isinstance(data, mem_access)
|
||||
|
||||
@staticmethod
|
||||
def mem_access_has_only(mem_access, names):
|
||||
if not X64.is_mem_acces(mem_access):
|
||||
raise ValueError("mem_access_has_only")
|
||||
for f in mem_access._fields:
|
||||
if getattr(mem_access, f) and f not in names:
|
||||
return False
|
||||
if "base" in names and mem_access.base is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def to_little_endian(i, size=64):
|
||||
pack = {8: 'B', 16 : 'H', 32 : 'I', 64 : 'Q'}
|
||||
s = pack[size]
|
||||
mask = (1 << size) - 1
|
||||
i = i & mask
|
||||
return struct.unpack("<" + s, struct.pack(">" + s, i))[0]
|
||||
|
||||
# Sub ModRM encoding
|
||||
|
||||
#class RexByte(object):
|
||||
# def __init__(self):
|
||||
# self.is_needed = False
|
||||
# self.pattern = BitArray(4, "0100")
|
||||
# self.w = BitArray(1, "0")
|
||||
# self.r = BitArray(1, "0")
|
||||
# self.x = BitArray(1, "0")
|
||||
# self.b = BitArray(1, "0")
|
||||
|
||||
|
||||
class SubModRM(object):
|
||||
def __init__(self):
|
||||
self.mod = BitArray(2, "")
|
||||
@@ -406,7 +432,7 @@ class ModRM_REG64__REG64(SubModRM):
|
||||
def match(cls, arg1, arg2):
|
||||
return (X64.is_reg(arg1) or X64.is_new_reg(arg1)) and (X64.is_reg(arg2) or X64.is_new_reg(arg2))
|
||||
|
||||
def __init__(self, arg1, arg2, reversed):
|
||||
def __init__(self, arg1, arg2, reversed, instr_state):
|
||||
super(ModRM_REG64__REG64, self).__init__()
|
||||
self.mod = BitArray(2, "11")
|
||||
self.is_rex_needed = True
|
||||
@@ -420,8 +446,10 @@ class ModRM_REG64__MEM(SubModRM):
|
||||
def match(cls, arg1, arg2):
|
||||
return (X64.is_reg(arg1) or X64.is_new_reg(arg1)) and X64.is_mem_acces(arg2)
|
||||
|
||||
def __init__(self, arg1, arg2, reversed):
|
||||
def __init__(self, arg1, arg2, reversed, instr_state):
|
||||
super(ModRM_REG64__MEM, self).__init__()
|
||||
if arg2.prefix is not None:
|
||||
instr_state.prefixes.append(x64_segment_selectors[arg2.prefix])
|
||||
# ARG1 : REG
|
||||
# ARG2 : [MEM]
|
||||
# this encode [rip + disp]
|
||||
@@ -510,16 +538,18 @@ class Slash(object):
|
||||
"reg = 7 for /7"
|
||||
self.reg = reg_order[reg_num]
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
if len(args) < 1:
|
||||
raise ValueError("Missing arg for Slash")
|
||||
# Reuse all the MODRm logique with the reg as our self.reg
|
||||
# The sens of param is strange I need to fix the `reversed` logique
|
||||
arg_consum, value, rex = ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM], has_direction_bit=False).accept_arg(previous, args[:1] + [self.reg] + args[1:])
|
||||
arg_consum, value, rex = ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM], has_direction_bit=False).accept_arg(args[:1] + [self.reg] + args[1:], instr_state)
|
||||
if value is None:
|
||||
return arg_consum, value, rex
|
||||
return arg_consum-1, value, rex
|
||||
|
||||
instr_state = collections.namedtuple('instr_state', ['previous', 'prefixes'])
|
||||
|
||||
class Instruction(object):
|
||||
encoding = []
|
||||
default_rex = BitArray(8, "")
|
||||
@@ -528,11 +558,12 @@ class Instruction(object):
|
||||
for type_encoding in self.encoding:
|
||||
args = list(initial_args)
|
||||
res = []
|
||||
prefix = []
|
||||
full_rex = self.default_rex
|
||||
if hasattr(self, "default_32_bits") and self.default_32_bits:
|
||||
full_rex = BitArray.from_int(8, 0x48)
|
||||
for element in type_encoding:
|
||||
arg_consum, value, rex = element.accept_arg(res, args)
|
||||
arg_consum, value, rex = element.accept_arg(args, instr_state(res, prefix))
|
||||
if arg_consum is None:
|
||||
break
|
||||
res.append(value)
|
||||
@@ -542,6 +573,7 @@ class Instruction(object):
|
||||
else: # if no break
|
||||
if args: # if still args: fail
|
||||
continue
|
||||
self.prefix = prefix
|
||||
self.value = sum(res, BitArray(0, ""))
|
||||
if any(full_rex.array):
|
||||
self.value = full_rex + self.value
|
||||
@@ -549,7 +581,8 @@ class Instruction(object):
|
||||
raise ValueError("Cannot encode <{0} {1}>:(".format(type(self).__name__, initial_args))
|
||||
|
||||
def get_code(self):
|
||||
return self.value.dump()
|
||||
prefix_opcode = b"".join(chr(p.PREFIX_VALUE) for p in self.prefix)
|
||||
return prefix_opcode + bytes(self.value.dump())
|
||||
|
||||
class DelayedJump(object):
|
||||
def __init__(self, type, label):
|
||||
@@ -624,7 +657,7 @@ class JmpImm(object):
|
||||
def __init__(self, sub):
|
||||
self.sub = sub
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
jump_size = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
@@ -669,9 +702,7 @@ class Jnb(JmpType):
|
||||
|
||||
|
||||
class Lea(Instruction):
|
||||
#default_rex = BitArray(8, "01001000")
|
||||
refuse_reverse = True
|
||||
#default_32_bits = False
|
||||
encoding = [(RawBits.from_int(8, 0x8d), ModRM([ModRM_REG64__MEM], accept_reverse=False, has_direction_bit=False))]
|
||||
|
||||
class Mov(Instruction):
|
||||
@@ -860,28 +891,25 @@ try:
|
||||
except ImportError:
|
||||
in_IDA = False
|
||||
|
||||
|
||||
def test_code():
|
||||
s = MultipleInstr()
|
||||
s += Mov('r8', 'r14')
|
||||
s += Label(':SUCE')
|
||||
s += Jnz(':END')
|
||||
s += Add('r14', 0x12345678)
|
||||
s += Dec('r9')
|
||||
s += Dec('rax')
|
||||
s += Jnz(':END')
|
||||
s += Mov('r8', 'rdx')
|
||||
s += Jnz(':END')
|
||||
s += Mov('r8', 'rdx')
|
||||
s += Jnz(':SUCE')
|
||||
s += Mov('r9', 'r10')
|
||||
s += Label(':END')
|
||||
s += Ret()
|
||||
return s
|
||||
|
||||
|
||||
|
||||
if in_IDA:
|
||||
def test_code():
|
||||
s = MultipleInstr()
|
||||
s += Mov('r8', 'r14')
|
||||
s += Label(':SUCE')
|
||||
s += Jnz(':END')
|
||||
s += Add('r14', 0x12345678)
|
||||
s += Dec('r9')
|
||||
s += Dec('rax')
|
||||
s += Jnz(':END')
|
||||
s += Mov('r8', 'rdx')
|
||||
s += Jnz(':END')
|
||||
s += Mov('r8', 'rdx')
|
||||
s += Jnz(':SUCE')
|
||||
s += Mov('r9', 'r10')
|
||||
s += Label(':END')
|
||||
s += Ret()
|
||||
return s
|
||||
|
||||
def reset():
|
||||
idc.MakeUnknown(idc.MinEA(), 0x1000, 0)
|
||||
for i in range(0x1000):
|
||||
|
||||
+186
-131
@@ -61,31 +61,108 @@ class BitArray(object):
|
||||
x = x & ((2 ** size) - 1)
|
||||
return cls(size, bin(x)[2:])
|
||||
|
||||
# Rule: bytes only !!!!
|
||||
# Prefix
|
||||
class Prefix(object):
|
||||
PREFIX_VALUE = None
|
||||
def __init__(self, next=None):
|
||||
self.next = next
|
||||
|
||||
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp'])
|
||||
def __add__(self, other):
|
||||
return type(self)(other)
|
||||
|
||||
def get_code(self):
|
||||
return chr(self.PREFIX_VALUE) + self.next.get_code()
|
||||
|
||||
def create_prefix(name, value):
|
||||
prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE' : value})
|
||||
setattr(sys.modules[__name__], name, prefix_type())
|
||||
|
||||
create_prefix('LockPrefix', 0xf0)
|
||||
create_prefix('Repne', 0xf2)
|
||||
create_prefix('Rep', 0xf3)
|
||||
create_prefix('SSPrefix', 0x36)
|
||||
create_prefix('CSPrefix', 0x2e)
|
||||
create_prefix('DSPrefix', 0x3e)
|
||||
create_prefix('ESPrefix', 0x26)
|
||||
create_prefix('FSPrefix', 0x64)
|
||||
create_prefix('GSPrefix', 0x65)
|
||||
create_prefix('OperandSizeOverride', 0x66)
|
||||
create_prefix('AddressSizeOverride', 0x67)
|
||||
|
||||
# Main informations about X86
|
||||
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp', 'prefix'])
|
||||
x86_regs = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
|
||||
x86_16bits_regs = ['AX', 'CX', 'DX', 'BX', 'SP', 'BP', 'SI', 'DI']
|
||||
|
||||
def create_displacement(base=None, index=None, scale=None, disp=0):
|
||||
x86_segment_selectors = {'CS' : CSPrefix, 'DS' : DSPrefix, 'ES' : ESPrefix, 'SS' : SSPrefix,
|
||||
'FS': FSPrefix, 'GS' : GSPrefix}
|
||||
|
||||
class X86(object):
|
||||
@staticmethod
|
||||
def is_reg(name):
|
||||
try:
|
||||
return name.upper() in x86_regs + x86_16bits_regs
|
||||
except AttributeError: # Not a string
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def reg_size(name):
|
||||
if name.upper() in x86_regs:
|
||||
return 32
|
||||
elif name.upper() in x86_16bits_regs:
|
||||
return 16
|
||||
else:
|
||||
raise ValueError("Unknow register <{0}>".format(name))
|
||||
|
||||
@staticmethod
|
||||
def is_mem_acces(data):
|
||||
return isinstance(data, mem_access)
|
||||
|
||||
@staticmethod
|
||||
def mem_access_has_only(mem_access, names):
|
||||
if not X86.is_mem_acces(mem_access):
|
||||
raise ValueError("mem_access_has_only")
|
||||
for f in mem_access._fields:
|
||||
v = getattr(mem_access, f)
|
||||
if v and f != 'prefix' and f not in names:
|
||||
return False
|
||||
if v is None and f in names:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None):
|
||||
"""Create an X86 memory access description"""
|
||||
if index is not None and scale is None:
|
||||
scale = 1
|
||||
if scale and index is None:
|
||||
raise ValueError("Cannot create displacement with scale and no index")
|
||||
if scale and index.upper() == "ESP":
|
||||
raise ValueError("Cannot create displacement with index == ESP")
|
||||
return mem_access(base, index, scale, disp)
|
||||
return mem_access(base, index, scale, disp, prefix)
|
||||
|
||||
def mem(data):
|
||||
"""Parse a memory access string"""
|
||||
"""Parse a memory access string of format [EXPR] or seg:[EXPR]
|
||||
EXPR may describe: BASE | INDEX * SCALE | DISPLACEMENT or any combinaison (in this order)
|
||||
"""
|
||||
if not isinstance(data, str):
|
||||
raise TypeError("mem need a string to parse")
|
||||
data = data.strip()
|
||||
prefix = None
|
||||
if not (data.startswith("[") and data.endswith("]")):
|
||||
raise ValueError("mem acces expect <[EXPR]>")
|
||||
if data[2] != ":":
|
||||
raise ValueError("mem acces expect <[EXPR]> or <seg:[EXPR]")
|
||||
prefix_name = data[:2].upper()
|
||||
if prefix_name not in x86_segment_selectors:
|
||||
raise ValueError("Unknow segment selector {0}".format(prefix_name))
|
||||
prefix = prefix_name
|
||||
data = data[3:]
|
||||
if not (data.startswith("[") and data.endswith("]")):
|
||||
raise ValueError("mem acces expect <[EXPR]> or <seg:[EXPR]")
|
||||
# A l'arrache.. j'aime pas le parsing de trucs
|
||||
data = data[1:-1]
|
||||
items = data.split("+")
|
||||
parsed_items = {}
|
||||
parsed_items = {'prefix' : prefix}
|
||||
for item in items:
|
||||
item = item.strip()
|
||||
# Index * scale
|
||||
@@ -99,6 +176,8 @@ def mem(data):
|
||||
index, scale = index.strip(), scale.strip()
|
||||
if not X86.is_reg(index):
|
||||
raise ValueError("Invalid index <{0}> in mem access".format(index))
|
||||
if X86.reg_size(index) == 16:
|
||||
raise NotImplementedError("16bits modrm")
|
||||
try:
|
||||
scale = int(scale, 0)
|
||||
except ValueError as e:
|
||||
@@ -108,6 +187,8 @@ def mem(data):
|
||||
else:
|
||||
# displacement / base / index alone
|
||||
if X86.is_reg(item):
|
||||
if X86.reg_size(item) == 16:
|
||||
raise NotImplementedError("16bits modrm")
|
||||
if not 'base' in parsed_items:
|
||||
parsed_items['base'] = item
|
||||
continue
|
||||
@@ -125,13 +206,14 @@ def mem(data):
|
||||
parsed_items['disp'] = disp
|
||||
return create_displacement(**parsed_items)
|
||||
|
||||
# Helper to get the BitArray associated to a register
|
||||
|
||||
class X86RegisterSelector(object):
|
||||
size = 3 # bits
|
||||
reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
|
||||
reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)}
|
||||
reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_regs)}
|
||||
reg_opcode.update({v : BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_16bits_regs)})
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
x = args[0]
|
||||
try:
|
||||
return (1, self.reg_opcode[x.upper()])
|
||||
@@ -142,28 +224,25 @@ class X86RegisterSelector(object):
|
||||
def get_reg_bits(cls, name):
|
||||
return cls.reg_opcode[name.upper()]
|
||||
|
||||
class RegisterEax(object):
|
||||
def accept_arg(self, previous, args):
|
||||
x = args[0]
|
||||
if isinstance(x, str) and x.upper() == 'EAX':
|
||||
return (1, BitArray(0, []))
|
||||
return None, None
|
||||
## Instruction Parameters
|
||||
|
||||
class FixedRegister(object):
|
||||
def __init__(self, register):
|
||||
self.reg = register.upper()
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
x = args[0]
|
||||
if isinstance(x, str) and x.upper() == self.reg:
|
||||
return (1, BitArray(0, []))
|
||||
return None, None
|
||||
|
||||
RegisterEax = lambda: FixedRegister('EAX')
|
||||
|
||||
class RawBits(BitArray):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
return (0, self)
|
||||
|
||||
# Immediat value logique
|
||||
# Immediat value logic
|
||||
# All 8/16 bits stuff are sign extended
|
||||
|
||||
class ImmediatOverflow(ValueError):
|
||||
@@ -192,19 +271,19 @@ def accept_as_32immediat(x):
|
||||
raise ImmediatOverflow("32bits signed Immediat overflow")
|
||||
|
||||
class Imm8(object):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
x = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
return (None, None)
|
||||
try:
|
||||
imm8 = accept_as_16immediat(x)
|
||||
imm8 = accept_as_8immediat(x)
|
||||
except ImmediatOverflow:
|
||||
return None, None
|
||||
return (1, BitArray.from_string(imm8))
|
||||
|
||||
class Imm16(object):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
x = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
@@ -216,7 +295,7 @@ class Imm16(object):
|
||||
return (1, BitArray.from_string(imm16))
|
||||
|
||||
class Imm32(object):
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
x = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
@@ -233,57 +312,37 @@ class ModRM(object):
|
||||
self.has_direction_bit = has_direction_bit
|
||||
self.sub = sub_modrm
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
if len(args) < 2:
|
||||
raise ValueError("Missing arg for modrm")
|
||||
arg1 = args[0]
|
||||
arg2 = args[1]
|
||||
for sub in self.sub:
|
||||
# Problem in reverse sens -> need to fix it
|
||||
#import pdb;pdb.set_trace()
|
||||
if sub.match(arg1, arg2):
|
||||
d = sub(arg1, arg2, 0)
|
||||
d = sub(arg1, arg2, 0, instr_state)
|
||||
if self.has_direction_bit:
|
||||
previous[0][-2] = d.direction
|
||||
instr_state.previous[0][-2] = d.direction
|
||||
return (2, d.mod + d.reg + d.rm + d.after)
|
||||
elif self.accept_reverse and sub.match(arg2, arg1):
|
||||
d = sub(arg2, arg1, 1)
|
||||
d = sub(arg2, arg1, 1, instr_state)
|
||||
if self.has_direction_bit:
|
||||
previous[0][-2] = d.direction
|
||||
instr_state.previous[0][-2] = d.direction
|
||||
return (2, d.mod + d.reg + d.rm + d.after)
|
||||
return (None, None)
|
||||
|
||||
class X86(object):
|
||||
@staticmethod
|
||||
def is_reg(name):
|
||||
try:
|
||||
return name.upper() in x86_regs
|
||||
except AttributeError: # Not a string
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def is_mem_acces(data):
|
||||
return isinstance(data, mem_access)
|
||||
|
||||
@staticmethod
|
||||
def mem_access_has_only(mem_access, names):
|
||||
if not X86.is_mem_acces(mem_access):
|
||||
raise ValueError("mem_access_has_only")
|
||||
for f in mem_access._fields:
|
||||
v = getattr(mem_access, f)
|
||||
if v and f not in names:
|
||||
return False
|
||||
if v is None and f in names:
|
||||
return False
|
||||
return True
|
||||
|
||||
class ModRM_REG__REG(object):
|
||||
@classmethod
|
||||
def match(cls, arg1, arg2):
|
||||
return X86.is_reg(arg1) and X86.is_reg(arg2)
|
||||
|
||||
def __init__(self, arg1, arg2, reversed):
|
||||
def __init__(self, arg1, arg2, reversed, instr_state):
|
||||
self.mod = BitArray(2, "11")
|
||||
if X86.reg_size(arg1) != X86.reg_size(arg2):
|
||||
raise ValueError("Register size mitmatch between {0} and {1}".format(arg1, arg2))
|
||||
if X86.reg_size(arg1) == 16:
|
||||
instr_state.prefixes.append(OperandSizeOverride)
|
||||
self.reg = X86RegisterSelector.get_reg_bits(arg2)
|
||||
self.rm = X86RegisterSelector.get_reg_bits(arg1)
|
||||
self.after = BitArray(0, "")
|
||||
@@ -294,12 +353,20 @@ class ModRM_REG__MEM(object):
|
||||
def match(cls, arg1, arg2):
|
||||
return X86.is_reg(arg1) and X86.is_mem_acces(arg2)
|
||||
|
||||
def __init__(self, arg1, arg2, reversed):
|
||||
def setup_reg_as_register(self, regname, instr_state):
|
||||
self.reg = X86RegisterSelector.get_reg_bits(regname)
|
||||
if X86.reg_size(regname) == 16:
|
||||
instr_state.prefixes.append(OperandSizeOverride)
|
||||
|
||||
def __init__(self, arg1, arg2, reversed, instr_state):
|
||||
# ARG1 : REG
|
||||
# ARG2 : [MEM]
|
||||
# ARG2 : prefix:[MEM]
|
||||
# Handle prefix:
|
||||
if arg2.prefix is not None:
|
||||
instr_state.prefixes.append(x86_segment_selectors[arg2.prefix])
|
||||
if X86.mem_access_has_only(arg2, ["disp"]):
|
||||
self.mod = BitArray(2, "00")
|
||||
self.reg = X86RegisterSelector.get_reg_bits(arg1)
|
||||
self.setup_reg_as_register(arg1, instr_state)
|
||||
self.rm = BitArray(3, "101")
|
||||
try:
|
||||
self.after = BitArray.from_string(accept_as_32immediat(arg2.disp))
|
||||
@@ -311,7 +378,7 @@ class ModRM_REG__MEM(object):
|
||||
# No index -> no scale -> no SIB
|
||||
FIRE_UP_SIB = (arg2.base and arg2.base.upper() in ["ESP", "EBP"]) or arg2.index
|
||||
if not FIRE_UP_SIB:
|
||||
self.reg = X86RegisterSelector.get_reg_bits(arg1)
|
||||
self.setup_reg_as_register(arg1, instr_state)
|
||||
self.rm = X86RegisterSelector.get_reg_bits(arg2.base)
|
||||
self.compute_displacement(arg2.disp)
|
||||
self.direction = not reversed
|
||||
@@ -325,7 +392,7 @@ class ModRM_REG__MEM(object):
|
||||
else:
|
||||
force_displacement = 0
|
||||
|
||||
self.reg = X86RegisterSelector.get_reg_bits(arg1)
|
||||
self.setup_reg_as_register(arg1, instr_state)
|
||||
self.rm = BitArray(3, "100")
|
||||
self.compute_displacement(arg2.disp, force_displacement)
|
||||
self.after = self.compute_sib(arg2) + self.after
|
||||
@@ -376,25 +443,28 @@ class Slash(object):
|
||||
"reg = 7 for /7"
|
||||
self.reg = x86_regs[reg_num]
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
def accept_arg(self, args, instr_state):
|
||||
if len(args) < 1:
|
||||
raise ValueError("Missing arg for Slash")
|
||||
# Reuse all the MODRm logique with the reg as our self.reg
|
||||
# The sens of param is strange I need to fix the `reversed` logique
|
||||
arg_consum, value = ModRM([ModRM_REG__REG, ModRM_REG__MEM], has_direction_bit=False).accept_arg(previous, args[:1] + [self.reg] + args[1:])
|
||||
arg_consum, value = ModRM([ModRM_REG__REG, ModRM_REG__MEM], has_direction_bit=False).accept_arg(args[:1] + [self.reg] + args[1:], instr_state)
|
||||
if value is None:
|
||||
return arg_consum, value
|
||||
return arg_consum-1, value
|
||||
|
||||
class Instruction(object):
|
||||
encoding = []
|
||||
instr_state = collections.namedtuple('instr_state', ['previous', 'prefixes'])
|
||||
|
||||
class Instruction(object):
|
||||
"""Base class of instructions, use `encoding` to find a valid way to assemble the instruction"""
|
||||
encoding = []
|
||||
def __init__(self, *initial_args):
|
||||
for type_encoding in self.encoding:
|
||||
args = list(initial_args)
|
||||
prefix = []
|
||||
res = []
|
||||
for element in type_encoding:
|
||||
arg_consum, value = element.accept_arg(res, args)
|
||||
arg_consum, value = element.accept_arg(args, instr_state(res, prefix))
|
||||
if arg_consum is None:
|
||||
break
|
||||
res.append(value)
|
||||
@@ -403,18 +473,23 @@ class Instruction(object):
|
||||
if args: # if still args: fail
|
||||
continue
|
||||
self.value = sum(res, BitArray(0, ""))
|
||||
self.prefix = prefix
|
||||
return
|
||||
raise ValueError("Cannot encode <{0} {1}>:(".format(type(self).__name__, initial_args))
|
||||
|
||||
def get_code(self):
|
||||
return bytes(self.value.dump())
|
||||
prefix_opcode = b"".join(chr(p.PREFIX_VALUE) for p in self.prefix)
|
||||
return prefix_opcode + bytes(self.value.dump())
|
||||
|
||||
# Jump helpers
|
||||
class DelayedJump(object):
|
||||
"""A jump to a label :NAME"""
|
||||
def __init__(self, type, label):
|
||||
self.type = type
|
||||
self.label = label
|
||||
|
||||
class JmpType(Instruction):
|
||||
"""Dispatcher between a real jump or DelayedJump if parameters is a label"""
|
||||
def __new__(cls, *initial_args):
|
||||
if len(initial_args) == 1:
|
||||
arg = initial_args[0]
|
||||
@@ -422,6 +497,45 @@ class JmpType(Instruction):
|
||||
return DelayedJump(cls, arg)
|
||||
return super(JmpType, cls).__new__(cls, *initial_args)
|
||||
|
||||
class JmpImm(object):
|
||||
"""Immediat parameters for Jump instruction
|
||||
Sub a specified size from the size to jump to `emulate` a jump from the begin address of the instruction"""
|
||||
accept_as_Ximmediat = None
|
||||
def __init__(self, sub):
|
||||
self.sub = sub
|
||||
|
||||
def accept_arg(self, args, instr_state):
|
||||
try:
|
||||
jump_size = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
return (None, None)
|
||||
jump_size -= self.sub
|
||||
try:
|
||||
jmp_imm = self.accept_as_Ximmediat(jump_size)
|
||||
except ImmediatOverflow:
|
||||
return (None, None)
|
||||
return (1, BitArray.from_string(jmp_imm))
|
||||
|
||||
class JmpImm8(JmpImm):
|
||||
accept_as_Ximmediat = staticmethod(accept_as_8immediat)
|
||||
|
||||
class JmpImm32(JmpImm):
|
||||
accept_as_Ximmediat = staticmethod(accept_as_32immediat)
|
||||
|
||||
## Instructions
|
||||
|
||||
class Jmp(JmpType):
|
||||
encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)),
|
||||
(RawBits.from_int(8, 0xe9), JmpImm32(5))]
|
||||
|
||||
class Jz(JmpType):
|
||||
encoding = [(RawBits.from_int(8, 0x74), JmpImm8(2)),
|
||||
(RawBits.from_int(16, 0x0f84), JmpImm32(6))]
|
||||
|
||||
class Jnz(JmpType):
|
||||
encoding = [(RawBits.from_int(8, 0x75), JmpImm8(2)),
|
||||
(RawBits.from_int(16, 0x0f85), JmpImm32(6))]
|
||||
|
||||
class Push(Instruction):
|
||||
encoding = [(RawBits.from_int(5, 0x50 >> 3), X86RegisterSelector()),
|
||||
(RawBits.from_int(8, 0x68), Imm32())]
|
||||
@@ -466,42 +580,6 @@ class In(Instruction):
|
||||
(RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now
|
||||
(RawBits.from_int(8, 0xed), FixedRegister('EAX'), FixedRegister('DX'))]
|
||||
|
||||
class JmpImm(object):
|
||||
accept_as_Ximmediat = None
|
||||
def __init__(self, sub):
|
||||
self.sub = sub
|
||||
|
||||
def accept_arg(self, previous, args):
|
||||
try:
|
||||
jump_size = int(args[0])
|
||||
except (ValueError, TypeError):
|
||||
return (None, None)
|
||||
jump_size -= self.sub
|
||||
try:
|
||||
jmp_imm = self.accept_as_Ximmediat(jump_size)
|
||||
except ImmediatOverflow:
|
||||
return (None, None)
|
||||
return (1, BitArray.from_string(jmp_imm))
|
||||
|
||||
class JmpImm8(JmpImm):
|
||||
accept_as_Ximmediat = staticmethod(accept_as_8immediat)
|
||||
|
||||
class JmpImm32(JmpImm):
|
||||
accept_as_Ximmediat = staticmethod(accept_as_32immediat)
|
||||
|
||||
|
||||
class Jmp(JmpType):
|
||||
encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)),
|
||||
(RawBits.from_int(8, 0xe9), JmpImm32(5))]
|
||||
|
||||
class Jz(JmpType):
|
||||
encoding = [(RawBits.from_int(8, 0x74), JmpImm8(2)),
|
||||
(RawBits.from_int(16, 0x0f84), JmpImm32(6))]
|
||||
|
||||
class Jnz(JmpType):
|
||||
encoding = [(RawBits.from_int(8, 0x75), JmpImm8(2)),
|
||||
(RawBits.from_int(16, 0x0f85), JmpImm32(6))]
|
||||
|
||||
class Xor(Instruction):
|
||||
encoding = [(RawBits.from_int(8, 0x31), ModRM([ModRM_REG__REG]))]
|
||||
|
||||
@@ -680,7 +758,6 @@ class MultipleInstr(object):
|
||||
return self
|
||||
|
||||
# IDA : import windows.native_exec.simple_x86 as x86
|
||||
|
||||
# IDA testing
|
||||
|
||||
try:
|
||||
@@ -690,38 +767,16 @@ try:
|
||||
except ImportError:
|
||||
in_IDA = False
|
||||
|
||||
#def test_code():
|
||||
# s = MultipleInstr()
|
||||
# s += Mov('EAX', 'EAX')
|
||||
# s += Mov('EAX', 'EAX')
|
||||
# s += Jnz(":SUCE")
|
||||
# s += Mov('EAX', 'EAX')
|
||||
# s += Cmp("Eax", "ESI")
|
||||
# s += Jnz(":SUCE")
|
||||
# s += Mov("ECX", "ECX")
|
||||
# s += Label(":SUCE")
|
||||
# s += Jnz(":LOL")
|
||||
# s += Jnz(":BITE")
|
||||
# s += Mov("EDX", "EDX")
|
||||
# s += Label(":LOL")
|
||||
# s += Mov('EDI', 'EDI')
|
||||
# s += Label(":BITE")
|
||||
# s += Mov('EDI', 'EDI')
|
||||
# s += Jnz(":SUCE")
|
||||
# s += Push("ECX")
|
||||
# s += Pop("EAX")
|
||||
# s += Ret()
|
||||
# return s
|
||||
|
||||
def test_code():
|
||||
s = MultipleInstr()
|
||||
s += Mov("Eax", "ESI")
|
||||
s += Inc("Ecx")
|
||||
s += Dec("edi")
|
||||
s += Ret()
|
||||
return s
|
||||
|
||||
if in_IDA:
|
||||
def test_code():
|
||||
s = MultipleInstr()
|
||||
s += Mov("Eax", "ESI")
|
||||
s += Inc("Ecx")
|
||||
s += Dec("edi")
|
||||
s += Ret()
|
||||
return s
|
||||
|
||||
def reset():
|
||||
idc.MakeUnknown(idc.MinEA(), 0x1000, 0)
|
||||
for i in range(0x1000):
|
||||
|
||||
@@ -54,6 +54,12 @@ class TestInstr(object):
|
||||
def compare_mem_access(self, memaccess, capres, cap_op):
|
||||
if cap_op.type != capstone.x86.X86_OP_MEM:
|
||||
raise AssertionError("Expected Memaccess <{0}> got {1}".format(memaccess, cap_op))
|
||||
if memaccess.prefix is not None and capres.prefix[1] != x64_segment_selectors[memaccess.prefix].PREFIX_VALUE:
|
||||
try:
|
||||
get_prefix = [n for n,x in x64_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0]
|
||||
except IndexError:
|
||||
get_prefix = None
|
||||
raise AssertionError("Expected Segment overide <{0}> got {1}".format(memaccess.prefix, get_prefix))
|
||||
cap_mem = cap_op.mem
|
||||
if memaccess.base is None and cap_mem.base != capstone.x86.X86_REG_INVALID:
|
||||
raise AssertionError("Unexpected memaccess base <{0}>".format(capres.reg_name(cap_mem.base)))
|
||||
@@ -75,8 +81,10 @@ TestInstr(Add)('RAX', mem('[RDI + 0x10]'))
|
||||
TestInstr(Add)('RAX', mem('[RSI + 0x7fffffff]'))
|
||||
TestInstr(Add)('RAX', mem('[RSI + -0x1]'))
|
||||
TestInstr(Add)('RAX', mem('[0x10]'))
|
||||
TestInstr(Add)('RAX', mem('fs:[0x10]'))
|
||||
TestInstr(Add)('RAX', mem('[RSI + RDI * 2]'))
|
||||
TestInstr(Add)('RAX', mem('[RSI + RDI * 2 + 0x10]'))
|
||||
TestInstr(Add)('RAX', mem('gs:[RSI + RDI * 2 + 0x10]'))
|
||||
TestInstr(Add)('RAX', mem('[R15 * 8 + 0x10]'))
|
||||
TestInstr(Add)('RAX', mem('[R9 + R8 * 2 + 0x7fffffff]'))
|
||||
TestInstr(Add)('RAX', mem('[R9 + R8 * 2 + -0x80000000]'))
|
||||
@@ -86,11 +94,14 @@ TestInstr(Xor)('R15', mem('[RAX + R8 * 2 + 0x11223344]'))
|
||||
TestInstr(Xor)('RAX', 'RAX')
|
||||
TestInstr(Cmp)('RAX', -1)
|
||||
TestInstr(Lea)('RAX', mem('[RAX + 1]'))
|
||||
TestInstr(Lea)('RAX', mem('fs:[RAX + 1]'))
|
||||
TestInstr(Mov)('RAX', mem('[0x1122334455667788]'))
|
||||
TestInstr(Mov)('RAX', mem('gs:[0x1122334455667788]'))
|
||||
TestInstr(Mov)('RAX', mem('gs:[0x60]'))
|
||||
TestInstr(Mov)('RCX', 0x1122334455667788)
|
||||
TestInstr(Mov)('RCX', -1)
|
||||
TestInstr(Mov, immediat_accepted=-1)('RCX', 0xffffffffffffffff)
|
||||
TestInstr(Mov)(mem('[0x1122334455667788]'), 'RAX')
|
||||
TestInstr(Mov)(mem('gs:[0x1122334455667788]'), 'RAX')
|
||||
TestInstr(Push)('R15')
|
||||
TestInstr(Push)(0x42)
|
||||
TestInstr(Push)(-1)
|
||||
|
||||
@@ -7,29 +7,29 @@ disassembleur.detail = True
|
||||
def disas(x):
|
||||
return list(disassembleur.disasm(x, 0))
|
||||
|
||||
|
||||
|
||||
class TestInstr(object):
|
||||
def __init__(self, instr_to_test):
|
||||
self.instr_to_test = instr_to_test
|
||||
|
||||
|
||||
def __call__(self, *args):
|
||||
res = bytes(self.instr_to_test(*args).get_code())
|
||||
capres_list = disas(res)
|
||||
if len(capres_list) != 1:
|
||||
raise AssertionError("Trying to disas an instruction resulted in multiple disassembled instrs")
|
||||
raise AssertionError("Trying to disas an instruction resulted in multiple disassembled instrs")
|
||||
capres = capres_list[0]
|
||||
print("{0} {1}".format(capres.mnemonic, capres.op_str))
|
||||
if len(res) != len(capres.bytes):
|
||||
raise AssertionError("Not all bytes have been used by the disassembler")
|
||||
self.compare_mnemo(capres)
|
||||
self.compare_args(args, capres)
|
||||
|
||||
|
||||
def compare_mnemo(self, capres):
|
||||
expected = self.instr_to_test.__name__.lower()
|
||||
if expected != str(capres.mnemonic):
|
||||
raise AssertionError("Expected menmo {0} got {1}".format(expected, str(capres.mnemonic)))
|
||||
return True
|
||||
|
||||
|
||||
def compare_args(self, args, capres):
|
||||
capres_op = list(capres.operands)
|
||||
if len(args) != len(capres_op):
|
||||
@@ -47,10 +47,16 @@ class TestInstr(object):
|
||||
self.compare_mem_access(op_args, capres, cap_op)
|
||||
else:
|
||||
raise ValueError("Unknow argument {0} of type {1}".format(op_args, type(op_args)))
|
||||
|
||||
|
||||
def compare_mem_access(self, memaccess, capres, cap_op):
|
||||
if cap_op.type != capstone.x86.X86_OP_MEM:
|
||||
raise AssertionError("Expected Memaccess <{0}> got {1}".format(memaccess, cap_op))
|
||||
if memaccess.prefix is not None and capres.prefix[1] != x86_segment_selectors[memaccess.prefix].PREFIX_VALUE:
|
||||
try:
|
||||
get_prefix = [n for n,x in x86_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0]
|
||||
except IndexError:
|
||||
get_prefix = None
|
||||
raise AssertionError("Expected Segment overide <{0}> got {1}".format(memaccess.prefix, get_prefix))
|
||||
cap_mem = cap_op.mem
|
||||
if memaccess.base is None and cap_mem.base != capstone.x86.X86_REG_INVALID:
|
||||
raise AssertionError("Unexpected memaccess base <{0}>".format(capres.reg_name(cap_mem.base)))
|
||||
@@ -59,13 +65,13 @@ class TestInstr(object):
|
||||
if memaccess.index is None and cap_mem.index != capstone.x86.X86_REG_INVALID:
|
||||
raise AssertionError("Unexpected memaccess index <{0}>".format(capres.reg_name(cap_mem.base)))
|
||||
if memaccess.index is not None and capres.reg_name(cap_mem.index) != memaccess.index.lower():
|
||||
raise AssertionError("Expected mem.index {0} got {1}".format(memaccess.index.lower(), capres.reg_name(cap_mem.index)))
|
||||
raise AssertionError("Expected mem.index {0} got {1}".format(memaccess.index.lower(), capres.reg_name(cap_mem.index)))
|
||||
if memaccess.scale != cap_mem.scale and not (memaccess.scale is None and cap_mem.scale == 1):
|
||||
raise AssertionError("Expected mem.scale {0} got {1}".format(memaccess.scale, cap_mem.scale))
|
||||
if memaccess.disp != cap_mem.disp:
|
||||
raise AssertionError("Expected mem.disp {0} got {1}".format(memaccess.disp, cap_mem.disp))
|
||||
|
||||
|
||||
|
||||
|
||||
TestInstr(Mov)('EAX', 'ESP')
|
||||
TestInstr(Mov)('ECX', mem('[EAX]'))
|
||||
TestInstr(Mov)('EDX', mem('[ECX + 0x10]'))
|
||||
@@ -74,6 +80,18 @@ TestInstr(Mov)('EDX', mem('[0x11223344]'))
|
||||
TestInstr(Mov)('EDX', mem('[ESP + EBP * 2 + 0x223344]'))
|
||||
TestInstr(Mov)(mem('[EBP + EBP * 2 + 0x223344]'), 'ESP')
|
||||
TestInstr(Mov)('ESI', mem('[ESI + EDI * 1]'))
|
||||
|
||||
TestInstr(Mov)('EAX', mem('fs:[0x30]'))
|
||||
TestInstr(Mov)('EDI', mem('gs:[EAX + ECX * 4]'))
|
||||
|
||||
TestInstr(Mov)('AX', 'AX')
|
||||
TestInstr(Mov)('SI', 'DI')
|
||||
|
||||
TestInstr(Mov)('AX', 'AX')
|
||||
TestInstr(Mov)('AX', mem('fs:[0x30]'))
|
||||
TestInstr(Mov)('AX', mem('fs:[EAX + 0x30]'))
|
||||
TestInstr(Mov)('AX', mem('fs:[EAX + ECX * 4+0x30]'))
|
||||
|
||||
TestInstr(Add)('EAX', 8)
|
||||
TestInstr(Add)('EAX', 0xffffffff)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user