From 53554f45ab48a9c840b73d0b0fc6ca0d3fb47598 Mon Sep 17 00:00:00 2001 From: hakril Date: Thu, 27 Jun 2024 14:22:40 +0200 Subject: [PATCH] Improve handling of Jmp/Call/Push/Pop --- tests/test_simple_x64.py | 4 ++ windows/native_exec/simple_x64.py | 63 ++++++++++++++++++++++++++++--- windows/native_exec/simple_x86.py | 2 +- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/tests/test_simple_x64.py b/tests/test_simple_x64.py index b916f45..768af05 100644 --- a/tests/test_simple_x64.py +++ b/tests/test_simple_x64.py @@ -232,6 +232,10 @@ def test_assembler(): CheckInstr(Pop)('RAX') + CheckInstr(Push, must_fail=True)('EAX') + CheckInstr(Pop)('R15') + CheckInstr(Pop)(mem("[ECX]")) + CheckInstr(Pop)(mem("[RCX]")) assert len(Pop("RAX").get_code()) == 1 diff --git a/windows/native_exec/simple_x64.py b/windows/native_exec/simple_x64.py index e440f2e..0f1a3ae 100644 --- a/windows/native_exec/simple_x64.py +++ b/windows/native_exec/simple_x64.py @@ -153,7 +153,7 @@ class X64(object): @staticmethod def is_reg(name): try: - return (name.upper() in reg_order) or X64.is_new_reg(name) or X64.is_32b_reg(name) + return X64.is_64b_reg(name) or X64.is_32b_reg(name) except AttributeError: # Not a string return False @@ -164,6 +164,13 @@ class X64(object): except AttributeError: # Not a string return False + @staticmethod + def is_64b_reg(name): + try: + return (name.upper() in x64_regs) + except AttributeError: # Not a string + return False + @staticmethod def is_32b_reg(name): try: @@ -656,6 +663,38 @@ class ModRM_REG__REG(SubModRM): self.setup_rm_as_register(arg1) self.direction = 0 +class ModRM_REG64__REG64(SubModRM): + """handle Reg64 Only, used by slash(x) that do not allow 32bits/rex (push / call / jmp)""" + @classmethod + def match(cls, arg1, arg2): + return X64.is_64b_reg(arg1) and X64.is_64b_reg(arg2) + + def setup_reg_as_register(self, name): + name = name.upper() + # register IS a 64b register + self.setup_as_64bit_operation() + self.reg = X64RegisterSelector.get_reg_bits(name) + if X64.is_new_reg(name): + self.is_rex_needed = True + self.rex[5] = 1 + + def setup_rm_as_register(self, name): + name = name.upper() + # register IS a 64b register + self.setup_as_64bit_operation() + + self.rm = X64RegisterSelector.get_reg_bits(name) + if X64.is_new_reg(name): + self.is_rex_needed = True + self.rex[7] = 1 + + def __init__(self, arg1, arg2, reversed, instr_state): + super(ModRM_REG64__REG64, self).__init__() + + self.mod = BitArray(2, "11") + self.setup_reg_as_register(arg2) + self.setup_rm_as_register(arg1) + self.direction = 0 class ModRM_REG64__MEM(SubModRM): @classmethod @@ -772,10 +811,13 @@ class REG64__MEM_Slash(ModRM_REG64__MEM): class Slash(object): "No idea for the name: represent the modRM for single args + encoding in reg (/7 in cmp in man intel)" - def __init__(self, reg_num): + # only64b seems to be the contrary ok Instr.default_32_bits use this as a base ? + # Make a Slash64 () ? + def __init__(self, reg_num, only64b=False): "reg = 7 for /7" self.reg = reg_order[reg_num] self.reg_num = reg_num + self.only64b = only64b def accept_arg(self, args, instr_state): if len(args) < 1: @@ -785,8 +827,13 @@ class Slash(object): injected_reg = self.reg if X64.is_32b_reg(args[0]): injected_reg = registers_64_to_32_bits[injected_reg] + if self.only64b: + modrm_reg_reg_type = ModRM_REG64__REG64 + else: + modrm_reg_reg_type = ModRM_REG__REG + try: - arg_consum, value, rex = ModRM([ModRM_REG__REG, REG64__MEM_Slash], has_direction_bit=False).accept_arg(args[:1] + [injected_reg] + args[1:], instr_state) + arg_consum, value, rex = ModRM([modrm_reg_reg_type, REG64__MEM_Slash], has_direction_bit=False).accept_arg(args[:1] + [injected_reg] + args[1:], instr_state) except ValueError as e: # Size mismatch return None, None, None @@ -868,11 +915,14 @@ class JmpType(Instruction): class Push(Instruction): encoding = [(RawBits.from_int(5, 0x50 >> 3), X64RegisterSelector()), (RawBits.from_int(8, 0x68), AnyImm32()), - (RawBits.from_int(8, 0xff), Slash(6))] + (RawBits.from_int(8, 0xff), Slash(6, only64b=True))] class Pop(Instruction): - encoding = [(RawBits.from_int(5, 0x58 >> 3), X64RegisterSelector())] + encoding = [(RawBits.from_int(5, 0x58 >> 3), X64RegisterSelector()), + (RawBits.from_int(8, 0x8f), Slash(0, only64b=True)) + + ] @@ -976,12 +1026,13 @@ class JmpImm32(JmpImm): class Call(JmpType): encoding = [(RawBits.from_int(8, 0xe8), JmpImm32(5)), - (RawBits.from_int(8, 0xff), Slash(2))] + (RawBits.from_int(8, 0xff), Slash(2, only64b=True))] class Jmp(JmpType): encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)), (RawBits.from_int(8, 0xe9), JmpImm32(5)), + (RawBits.from_int(8, 0xff), Slash(4, only64b=True)), (RawBits.from_int(13, 0xffe0 >> 3), X64RegisterSelector())] diff --git a/windows/native_exec/simple_x86.py b/windows/native_exec/simple_x86.py index 2cbe3b2..873692f 100644 --- a/windows/native_exec/simple_x86.py +++ b/windows/native_exec/simple_x86.py @@ -698,7 +698,6 @@ class JmpImm8(JmpImm): class JmpImm32(JmpImm): accept_as_Ximmediat = staticmethod(accept_as_32immediat) - # Instructions class Call(JmpType): @@ -709,6 +708,7 @@ class Call(JmpType): class Jmp(JmpType): encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)), (RawBits.from_int(8, 0xe9), JmpImm32(5)), + (RawBits.from_int(8, 0xff), Slash(4)), (RawBits.from_int(8, 0xea), SegmentSelectorAbsoluteAddr())]