From bac3dcb55b481d9e53f590c6ec2fac38e8ef57ac Mon Sep 17 00:00:00 2001 From: hakril Date: Wed, 5 Feb 2020 23:57:34 +0100 Subject: [PATCH] Fix simple_x64 REX for python3 --- tests/test_simple_x64.py | 21 +++++++++++---- tests/test_simple_x86.py | 14 +++++++--- windows/native_exec/simple_x64.py | 43 ++++++++++++++++++++++++++++--- windows/native_exec/simple_x86.py | 8 ++++-- windows/pipe.py | 1 - 5 files changed, 73 insertions(+), 14 deletions(-) diff --git a/tests/test_simple_x64.py b/tests/test_simple_x64.py index 01fb65f..b83864d 100644 --- a/tests/test_simple_x64.py +++ b/tests/test_simple_x64.py @@ -8,6 +8,8 @@ import windows.native_exec.simple_x64 as x64 from windows.native_exec.simple_x64 import * del Test # Prevent pytest warning +from windows.pycompat import int_types + if capstone: disassembleur = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64) disassembleur.detail = True @@ -27,8 +29,6 @@ def disas(x): mnemonic_name_exception = {'movabs': 'mov'} - - class CheckInstr(object): def __init__(self, instr_to_test, expected_result=None, immediat_accepted=None, must_fail=None, debug=False): self.instr_to_test = instr_to_test @@ -87,7 +87,7 @@ class CheckInstr(object): raise AssertionError("Expected args {0} operands got {1}".format(op_args, capres_op)) if op_args.lower() != capres.reg_name(cap_op.reg).lower(): raise AssertionError("Expected register <{0}> got {1}".format(op_args.lower(), capres.reg_name(cap_op.reg).lower())) - elif isinstance(op_args, (int, long)): + elif isinstance(op_args, int_types): if (op_args != cap_op.imm) and not (self.immediat_accepted and self.immediat_accepted == cap_op.imm): raise AssertionError("Expected Immediat <{0}> got {1}".format(op_args, cap_op.imm)) elif isinstance(op_args, mem_access): @@ -272,13 +272,18 @@ def test_assembler(): CheckInstr(Add, must_fail=True)('RAX', 0xffffffff) + # Test some prefix / REP + assert (x64.Rep + x64.Nop()).get_code() == b"\xf3\x90" + assert (x64.GSPrefix + x64.Nop()).get_code() == b"\x65\x90" + assert (x64.OperandSizeOverride + x64.Nop()).get_code() == b"\x66\x90" + assert (x64.Repne + x64.Nop()).get_code() == b"\xf2\x90" code = MultipleInstr() code += Nop() code += Rep + Nop() code += Ret() print(repr(code.get_code())) - assert code.get_code() == "\x90\xf3\x90\xc3" + assert code.get_code() == b"\x90\xf3\x90\xc3" def test_simple_x64_raw_instruction(): # Test the fake instruction "raw" @@ -296,8 +301,14 @@ def test_x64_multiple_instr_add_instr_and_str(): res += "ret; ret; label :offset_3; ret" res += x64.Nop() res += x64.Label(":offset_5") - assert res.get_code() == "\x90\xc3\xc3\xc3\x90" + assert res.get_code() == b"\x90\xc3\xc3\xc3\x90" assert res.labels == {":offset_3": 3, ":offset_5": 5} +def test_x64_instr_multiply(): + res = x64.MultipleInstr() + res += (x64.Nop() * 5) + res += x64.Ret() + assert res.get_code() == b"\x90\x90\x90\x90\x90\xc3" + if __name__ == "__main__": test_assembler() diff --git a/tests/test_simple_x86.py b/tests/test_simple_x86.py index c115fb0..aa773fa 100644 --- a/tests/test_simple_x86.py +++ b/tests/test_simple_x86.py @@ -9,6 +9,8 @@ import windows.native_exec.simple_x86 as x86 from windows.native_exec.simple_x86 import * del Test # Prevent pytest warning +from windows.pycompat import int_types + VERBOSE = False if capstone: @@ -77,7 +79,7 @@ class CheckInstr(object): raise AssertionError("Expected args {0} operands got {1}".format(op_args, capres_op)) if op_args.lower() != capres.reg_name(cap_op.reg).lower(): raise AssertionError("Expected register <{0}> got {1}".format(op_args.lower(), capres.reg_name(cap_op.reg).lower())) - elif isinstance(op_args, (int, long)): + elif isinstance(op_args, int_types): if (op_args != cap_op.imm) and not (self.immediat_accepted and self.immediat_accepted == cap_op.imm): raise AssertionError("Expected Immediat <{0}> got {1}".format(op_args, cap_op.imm)) elif isinstance(op_args, mem_access): @@ -226,7 +228,7 @@ def test_assembler(): code += Rep + Nop() code += Ret() print(repr(code.get_code())) - assert code.get_code() == "\x90\xf3\x90\xc3" + assert code.get_code() == b"\x90\xf3\x90\xc3" def test_simple_x64_raw_instruction(): # Test the fake instruction "raw" @@ -239,9 +241,15 @@ def test_x86_multiple_instr_add_instr_and_str(): res += "ret; ret; label :offset_3; ret" res += x86.Nop() res += x86.Label(":offset_5") - assert res.get_code() == "\x90\xc3\xc3\xc3\x90" + assert res.get_code() == b"\x90\xc3\xc3\xc3\x90" assert res.labels == {":offset_3": 3, ":offset_5": 5} +def test_x86_instr_multiply(): + res = x86.MultipleInstr() + res += (x86.Nop() * 5) + res += x86.Ret() + assert res.get_code() == b"\x90\x90\x90\x90\x90\xc3" + if capstone is None: test_assembler = pytest.mark.skip("Capstone not installed")(test_assembler) diff --git a/windows/native_exec/simple_x64.py b/windows/native_exec/simple_x64.py index 27cd905..632968d 100644 --- a/windows/native_exec/simple_x64.py +++ b/windows/native_exec/simple_x64.py @@ -1,8 +1,19 @@ +import sys import collections import struct +import binascii DEBUG = False + +# py3 +is_py3 = (sys.version_info.major >= 3) +if is_py3: + basestring = str + int_types = int +else: + int_types = (int, long) + class BitArray(object): def __init__(self, size, bits): self.size = size @@ -73,6 +84,16 @@ class BitArray(object): def copy(self): return type(self)(self.size, self.array) + def __eq__(self, other): + if not isinstance(other, BitArray): + return NotImplemented + return self.array == other.array + + def __ne__(self, other): + if not isinstance(other, BitArray): + return NotImplemented + return self.array != other.array + # Prefix class Prefix(object): @@ -84,9 +105,15 @@ class Prefix(object): def __add__(self, other): return type(self)(other) + def get_code_py3(self): + return bytes([self.PREFIX_VALUE]) + self.next.get_code() + def get_code(self): return chr(self.PREFIX_VALUE) + self.next.get_code() + if is_py3: + get_code = get_code_py3 + def create_prefix(name, value): prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE': value}) @@ -763,6 +790,9 @@ class Instruction(object): default_rex = BitArray.from_int(8, 0x40) def __init__(self, *initial_args): + # if (type(self) is Push): + # import pdb;pdb.set_trace() + for type_encoding in self.encoding: args = list(initial_args) res = [] @@ -783,7 +813,7 @@ class Instruction(object): continue self.prefix = prefix self.value = sum(res, BitArray(0, "")) - if str(full_rex.dump()) != "\x40": + if full_rex != self.default_rex: self.value = full_rex + self.value return raise ValueError("Cannot encode <{0} {1}>:(".format(type(self).__name__, initial_args)) @@ -792,8 +822,15 @@ class Instruction(object): prefix_opcode = b"".join(chr(p.PREFIX_VALUE) for p in self.prefix) return prefix_opcode + bytes(self.value.dump()) + def get_code_py3(self): + prefix_opcode = b"".join(bytes([p.PREFIX_VALUE]) for p in self.prefix) + return prefix_opcode + bytes(self.value.dump()) + + if is_py3: + get_code = get_code_py3 + def __mul__(self, value): - if not isinstance(value, (int, long)): + if not isinstance(value, int_types): return NotImplemented res = MultipleInstr() for i in range(value): @@ -1071,7 +1108,7 @@ class Raw(Instruction): if len(initial_args) != 1: raise ValueError("raw 'opcode' only accept one argument") # Accept space - self.data = initial_args[0].replace(" ", "").decode("hex") + self.data = binascii.unhexlify(initial_args[0].replace(" ", "")) def get_code(self): return self.data diff --git a/windows/native_exec/simple_x86.py b/windows/native_exec/simple_x86.py index 4e6badf..a090ef9 100644 --- a/windows/native_exec/simple_x86.py +++ b/windows/native_exec/simple_x86.py @@ -1,11 +1,15 @@ import sys import collections import struct +import binascii # py3 is_py3 = (sys.version_info.major >= 3) if is_py3: basestring = str + int_types = int +else: + int_types = (int, long) class BitArray(object): def __init__(self, size, bits): @@ -633,7 +637,7 @@ class Instruction(object): # return res def __mul__(self, value): - if not isinstance(value, (int, long)): + if not isinstance(value, int_types): return NotImplemented res = MultipleInstr() for i in range(value): @@ -910,7 +914,7 @@ class Raw(Instruction): if len(initial_args) != 1: raise ValueError("raw 'opcode' only accept one argument") # Accept space - self.data = initial_args[0].replace(" ", "").decode("hex") + self.data = binascii.unhexlify(initial_args[0].replace(" ", "")) def get_code(self): return self.data diff --git a/windows/pipe.py b/windows/pipe.py index 89a75a6..62f9677 100644 --- a/windows/pipe.py +++ b/windows/pipe.py @@ -54,7 +54,6 @@ class PipeConnection(object): # Cannot inherit: crash the interpreter gdef.PIPE_UNLIMITED_INSTANCES, cls.BUFFER_SIZE, cls.BUFFER_SIZE, gdef.NMPWAIT_WAIT_FOREVER, security_attributes ) - import pdb;pdb.set_trace() return cls.from_handle(pipehandle, name=addr, server=True) @classmethod