Skip to content

Commit 328735b

Browse files
authored
Merge pull request #54 from hakril/improve_simple_x64
Implem x64 mem_access [RIP+DISP32]
2 parents 275d054 + feafb52 commit 328735b

File tree

4 files changed

+71
-15
lines changed

4 files changed

+71
-15
lines changed

tests/test_simple_x64.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,23 @@ def test_assembler():
258258
CheckInstr(Not)('RAX')
259259
CheckInstr(Not)(mem('[RAX]'))
260260

261+
# Check RIP relative instructions
262+
CheckInstr(Mov)(mem('[rip + 0x41414141]'), 0x42424242)
263+
CheckInstr(Mov)(mem('[rip + 0x41414141]'), 'R8')
264+
CheckInstr(Mov)("RAX", mem('[rip + 0x2]'))
265+
CheckInstr(Mov)("RAX", mem('[rip + -5]'))
266+
CheckInstr(Mov)("RAX", mem('[rip + 0x41414141]'))
267+
CheckInstr(Lea)("RAX", mem('[rip + 0x2]'))
268+
CheckInstr(Lea)("RAX", mem('[rip + -5]'))
269+
CheckInstr(Lea)("EAX", mem('[rip + 0x2]'))
270+
CheckInstr(Lea)("R15", mem('[rip + 0x2]'))
271+
272+
with pytest.raises(ValueError):
273+
CheckInstr(Mov, must_fail=True)('RAX', mem('[RIP + RCX]')) # Invalid mem parsing
274+
with pytest.raises(ValueError):
275+
CheckInstr(Mov, must_fail=True)('REX', mem('[RIP * 2]')) # Invalid mem parsing
276+
with pytest.raises(ValueError):
277+
CheckInstr(Mov, must_fail=True)('REX', mem('[RCX + RIP]')) # Invalid mem parsing
261278

262279
CheckInstr(ScasB, expected_result="scasb al, byte ptr [rdi]")()
263280
CheckInstr(ScasW, expected_result="scasw ax, word ptr [rdi]")()
@@ -297,6 +314,10 @@ def test_simple_x64_raw_instruction():
297314
# By emetting a multi-char nop manually
298315
CheckInstr(Raw, expected_result="nop word ptr [rax + rax]")("66 0F 1F 84 00 00 00 00 00")
299316

317+
def test_simple_x64_assemble_raw_one_byte():
318+
# Test the fake instruction "raw" inside an assemble that may translate str to int
319+
assert x64.assemble("ret; raw 90; ret") == b"\xc3\x90\xc3"
320+
300321
def test_x64_32b_register_lower_upper():
301322
assert x64.assemble("mov eax, 42") == x64.assemble("mov EAX, 42")
302323
assert x64.Mov("eax", 42).get_code() == x64.Mov("EAX", 42).get_code()

tests/test_simple_x86.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ def test_simple_x64_raw_instruction():
246246
# By emetting a multi-char nop manually
247247
CheckInstr(Raw, expected_result="nop word ptr [eax + eax]")("66 0F 1F 84 00 00 00 00 00")
248248

249+
def test_simple_x86_assemble_raw_one_byte():
250+
# Test the fake instruction "raw" inside an assemble that may translate str to int
251+
assert x86.assemble("ret; raw 90; ret") == b"\xc3\x90\xc3"
252+
249253
def test_x86_multiple_instr_add_instr_and_str():
250254
res = x86.MultipleInstr()
251255
res += x86.Nop()

windows/native_exec/simple_x64.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def create_prefix(name, value):
136136
reg_order = ['RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI']
137137
new_reg_order = ['R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15']
138138
x64_regs = reg_order + new_reg_order
139+
# Rip is now addressable but is a special case
139140

140141
registers_32_bits = {'R15D': 'R15', 'R14D': 'R14', 'ESP': 'RSP', 'R9D': 'R9',
141142
'EDI': 'RDI', 'R11D': 'R11', 'R8D': 'R8', 'R10D': 'R10', 'EAX': 'RAX',
@@ -170,6 +171,10 @@ def is_32b_reg(name):
170171
except AttributeError: # Not a string
171172
return False
172173

174+
@staticmethod
175+
def is_rip(name):
176+
return name is not None and name.upper() == "RIP"
177+
173178
@staticmethod
174179
def is_mem_acces(data):
175180
return isinstance(data, mem_access)
@@ -201,6 +206,9 @@ def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None):
201206
raise ValueError("Cannot create displacement with scale and no index")
202207
if scale and index.upper() == "RSP":
203208
raise ValueError("Cannot create displacement with index == RSP")
209+
if base and X64.is_rip(base):
210+
if index is not None or scale is not None:
211+
raise ValueError("Bad use of RIP in mem expression <{0}> : only [RIP+DISP32] is possible".format(data))
204212
return mem_access(base, index, scale, disp, prefix)
205213

206214

@@ -251,10 +259,14 @@ def mem(data):
251259
parsed_items['index'] = index
252260
else:
253261
# displacement / base / index alone
254-
if X64.is_reg(item):
262+
if X64.is_reg(item) or X64.is_rip(item):
255263
if 'base' not in parsed_items:
256264
parsed_items['base'] = item
257265
continue
266+
# RIP is only usable on the format [RIP+DISP32]
267+
# So no index when base is RIP & no index as RIP
268+
if X64.is_rip(item) or X64.is_rip(parsed_items['base']):
269+
raise ValueError("Bad use of RIP in mem expression <{0}> : only [RIP+DISP32] is possible".format(data))
258270
# Already have base + index -> cannot avec another register in expression
259271
if 'index' in parsed_items:
260272
raise ValueError("Multiple index / index*scale in mem expression <{0}>".format(data))
@@ -656,18 +668,18 @@ def __init__(self, arg1, arg2, reversed, instr_state):
656668
instr_state.prefixes.append(x64_segment_selectors[arg2.prefix])
657669
# # ARG1 : REG
658670
# # ARG2 : [MEM]
659-
# # this encode [rip + disp]
660-
# # TODO :)
661-
# if X64.mem_access_has_only(arg2, ["disp"]):
662-
# self.mod = BitArray(2, "00")
663-
# self.setup_reg_as_register(arg1)
664-
# self.rm = BitArray(3, "101")
665-
# try:
666-
# self.after = BitArray.from_string(accept_as_32immediat(arg2.disp))
667-
# except ImmediatOverflow:
668-
# raise ImmediatOverflow("Interger32 overflow for displacement {0}".format(hex(arg2.disp)))
669-
# self.direction = not reversed
670-
# return
671+
672+
# Special case [RIP+DISP32]
673+
if X64.is_rip(arg2.base):
674+
self.mod = BitArray(2, "00")
675+
self.setup_reg_as_register(arg1)
676+
self.rm = BitArray(3, "101")
677+
try:
678+
self.after = BitArray.from_string(accept_as_32immediat(arg2.disp))
679+
except ImmediatOverflow:
680+
raise ImmediatOverflow("Interger32 overflow for displacement {0}".format(hex(arg2.disp)))
681+
self.direction = not reversed
682+
return
671683

672684
# Those registers cannot be addressed without SIB
673685
FIRE_UP_SIB = not arg2.base or arg2.base.upper() in ["RSP", "RBP"] or arg2.index
@@ -1098,6 +1110,8 @@ class Retf(Instruction):
10981110
default_32_bits = True
10991111
encoding = [(RawBits.from_int(8, 0xcb),)]
11001112

1113+
class Retfq(Instruction): # Give an explicit name to retf-64bits as it cannot be guessed from the mnemonic alone
1114+
encoding = [(RawBits.from_int(8, 0x48), RawBits.from_int(8, 0xcb))]
11011115

11021116
class Retf32(Instruction):
11031117
encoding = [(RawBits.from_int(8, 0xcb),)]
@@ -1115,11 +1129,15 @@ def JmpAt(addr):
11151129

11161130
class Raw(Instruction):
11171131
"""Output raw data"""
1132+
11181133
def __init__(self, *initial_args):
11191134
if len(initial_args) != 1:
11201135
raise ValueError("raw 'opcode' only accept one argument")
1121-
# Accept space
1122-
self.data = binascii.unhexlify(initial_args[0].replace(" ", ""))
1136+
if isinstance(initial_args[0], int):
1137+
self.data = struct.pack("B", initial_args[0])
1138+
else:
1139+
# Accept space
1140+
self.data = binascii.unhexlify(initial_args[0].replace(" ", ""))
11231141

11241142
def get_code(self):
11251143
return self.data
@@ -1300,6 +1318,12 @@ def assemble_instructions_generator(str):
13001318
except:
13011319
raise ValueError("Unknow mnemonic <{0}>".format(mnemo))
13021320

1321+
if issubclass(instr_object, Raw):
1322+
# Raw should received the raw buffer as it expect encoded hex
1323+
# The transformation may transform 'raw 9090' (nopnop) as 0n9090
1324+
# If other fake-instr need this : make a class attribute
1325+
yield instr_object(*args_raw)
1326+
continue
13031327
args = []
13041328
if args_raw:
13051329
for arg in args_raw[0].split(","):

windows/native_exec/simple_x86.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,13 @@ def assemble_instructions_generator(str):
11281128
except:
11291129
raise ValueError("Unknow mnemonic <{0}>".format(mnemo))
11301130

1131+
if issubclass(instr_object, Raw):
1132+
# Raw should received the raw buffer as it expect encoded hex
1133+
# The transformation may transform 'raw 9090' (nopnop) as 0n9090
1134+
# If other fake-instr need this : make a class attribute
1135+
yield instr_object(*args_raw)
1136+
continue
1137+
11311138
args = []
11321139
if args_raw:
11331140
for arg in args_raw[0].split(","):

0 commit comments

Comments
 (0)